Files
mai-bot/dashboard/dist/assets/mcp-settings-6oaCtDHj.js

4 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import{j as e,r as m}from"./router-zNjPR4CY.js";import{E as Y,M as q,W as G,U as J,S as Q,B as j,X as z,Y as A,C as R,b as T,d as Z,e as ee,f as E,I as g}from"./index-CuOHsLf7.js";import{B}from"./badge-CDs67obV.js";import{K as V}from"./key-value-editor-D1uUqXIn.js";import{S as H,a as I,b as D,c as O,d as b}from"./select-DGqIoF9r.js";import{S as se}from"./switch-Kc2EZ0Ga.js";import{T as te}from"./textarea-8PIujbf-.js";import{c as ae,f as P,D as re}from"./BotInfoSection-D3nfOJyA.js";import"./slider-mDhSC-zv.js";import{R as ne,u as oe,a as ie}from"./restart-overlay-B44c6hlE.js";import{ah as ce,a6 as le,ai as U,t as me,w as de,c as ue,v as he}from"./icons-DTcdLw9j.js";import"./misc-BwRzHX8c.js";import"./radix-C-ZuImoP.js";import"./utils-DjBw3JGv.js";import"./separator-B_DSOSdz.js";import"./popover-DSuRLFDH.js";import"./progress-C6-hh8fF.js";import"./system-api-DeeJapvB.js";const v={name:"",enabled:!0,transport:"stdio",command:"",args:[],env:{},url:"",headers:{},http_timeout_seconds:30,read_timeout_seconds:300,authorization:{mode:"none",bearer_token:""}};function y(t){return!t||typeof t!="object"||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).map(([i,s])=>[i,String(s??"")]))}function xe(t,i){const s=t&&typeof t=="object"&&!Array.isArray(t)?t:{},c=s.authorization&&typeof s.authorization=="object"&&!Array.isArray(s.authorization)?s.authorization:{},d=s.transport==="streamable_http"?"streamable_http":"stdio";return{...v,name:typeof s.name=="string"?s.name:`mcp-server-${i+1}`,enabled:typeof s.enabled=="boolean"?s.enabled:v.enabled,transport:d,command:typeof s.command=="string"?s.command:"",args:Array.isArray(s.args)?s.args.map(u=>String(u??"")):[],env:y(s.env),url:typeof s.url=="string"?s.url:"",headers:y(s.headers),http_timeout_seconds:typeof s.http_timeout_seconds=="number"?s.http_timeout_seconds:v.http_timeout_seconds,read_timeout_seconds:typeof s.read_timeout_seconds=="number"?s.read_timeout_seconds:v.read_timeout_seconds,authorization:{mode:c.mode==="bearer"?"bearer":"none",bearer_token:typeof c.bearer_token=="string"?c.bearer_token:""}}}function pe(t){return Array.isArray(t)?t.map((i,s)=>xe(i,s)):[]}function $(t,i,s){const c=t&&typeof t=="object"&&!Array.isArray(t)?t:{},[d,...u]=i;return d?u.length===0?{...c,[d]:s}:{...c,[d]:$(c[d],u,s)}:c}function fe({servers:t,onChange:i}){const s=(n,r)=>{i(t.map((a,p)=>p===n?{...a,...r}:a))},c=(n,r)=>{const a=t[n];a&&s(n,{authorization:{...a.authorization,...r}})},d=()=>{i([...t,{...v,name:`mcp-server-${t.length+1}`}])},u=n=>{const r=t[n];if(!r)return;const a={...r,name:`${r.name||"mcp-server"}-copy`,args:[...r.args],env:{...r.env},headers:{...r.headers},authorization:{...r.authorization}};i([...t.slice(0,n+1),a,...t.slice(n+1)])},x=n=>{i(t.filter((r,a)=>a!==n))};return e.jsxs(R,{children:[e.jsx(T,{className:"space-y-3",children:e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(me,{className:"h-5 w-5 text-muted-foreground"}),e.jsx(Z,{className:"text-lg",children:"MCP 服务"}),e.jsxs(B,{variant:"secondary",className:"text-xs",children:[t.length," 个"]})]}),e.jsx(ee,{children:"这里会写入 mcp.servers。stdio 用命令启动本地服务streamable_http 连接远程 MCP 端点。"})]}),e.jsxs(j,{type:"button",size:"sm",onClick:d,children:[e.jsx(de,{className:"mr-1 h-4 w-4"}),"添加服务"]})]})}),e.jsx(E,{className:"space-y-3",children:t.length===0?e.jsx("div",{className:"rounded-lg border border-dashed bg-muted/20 px-4 py-8 text-center text-sm text-muted-foreground",children:"尚未配置 MCP 服务。添加一个服务后MaiSaka 可以调用它暴露的工具。"}):t.map((n,r)=>e.jsxs(R,{className:"border-border/70 bg-muted/20 shadow-none",children:[e.jsx(T,{className:"space-y-3 px-4 py-3",children:e.jsxs("div",{className:"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",children:[e.jsxs("div",{className:"flex min-w-0 flex-1 items-center gap-3",children:[e.jsx(se,{checked:n.enabled,onCheckedChange:a=>s(r,{enabled:a})}),e.jsx("div",{className:"min-w-0 flex-1",children:e.jsx(g,{value:n.name,onChange:a=>s(r,{name:a.target.value}),placeholder:"服务名称,必须唯一",className:"h-8 font-medium"})}),e.jsx(B,{variant:n.enabled?"default":"secondary",className:"shrink-0 text-[10px]",children:n.enabled?"启用":"禁用"})]}),e.jsxs("div",{className:"flex shrink-0 items-center gap-2",children:[e.jsx(j,{type:"button",variant:"ghost",size:"icon",className:"h-8 w-8",onClick:()=>u(r),title:"复制服务",children:e.jsx(ue,{className:"h-4 w-4"})}),e.jsx(j,{type:"button",variant:"ghost",size:"icon",className:"h-8 w-8 text-destructive hover:text-destructive",onClick:()=>x(r),title:"删除服务",children:e.jsx(he,{className:"h-4 w-4"})})]})]})}),e.jsxs(E,{className:"space-y-4 px-4 pb-4 pt-0",children:[e.jsxs("div",{className:"grid gap-3 md:grid-cols-[12rem_1fr]",children:[e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-medium text-muted-foreground",children:"传输方式"}),e.jsxs(H,{value:n.transport,onValueChange:a=>s(r,{transport:a}),children:[e.jsx(I,{children:e.jsx(D,{})}),e.jsxs(O,{children:[e.jsx(b,{value:"stdio",children:"stdio"}),e.jsx(b,{value:"streamable_http",children:"streamable_http"})]})]})]}),n.transport==="stdio"?e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-medium text-muted-foreground",children:"启动命令"}),e.jsx(g,{value:n.command,onChange:a=>s(r,{command:a.target.value}),placeholder:"例如 uvx、npx、python"})]}):e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-medium text-muted-foreground",children:"服务 URL"}),e.jsx(g,{value:n.url,onChange:a=>s(r,{url:a.target.value}),placeholder:"https://example.com/mcp"})]})]}),n.transport==="stdio"?e.jsxs("div",{className:"grid gap-3 lg:grid-cols-2",children:[e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-medium text-muted-foreground",children:"命令参数"}),e.jsx(te,{value:n.args.join(`
`),onChange:a=>s(r,{args:a.target.value.split(`
`).map(p=>p.trim()).filter(p=>p.length>0)}),rows:4,placeholder:"每行一个参数"})]}),e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-medium text-muted-foreground",children:"环境变量"}),e.jsx(V,{value:n.env,onChange:a=>s(r,{env:y(a)})})]})]}):e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"grid gap-3 md:grid-cols-2",children:[e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-medium text-muted-foreground",children:"认证模式"}),e.jsxs(H,{value:n.authorization.mode,onValueChange:a=>c(r,{mode:a}),children:[e.jsx(I,{children:e.jsx(D,{})}),e.jsxs(O,{children:[e.jsx(b,{value:"none",children:"none"}),e.jsx(b,{value:"bearer",children:"bearer"})]})]})]}),n.authorization.mode==="bearer"&&e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-medium text-muted-foreground",children:"Bearer Token"}),e.jsx(g,{type:"password",value:n.authorization.bearer_token,onChange:a=>c(r,{bearer_token:a.target.value}),placeholder:"HTTP Bearer Token"})]})]}),e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-medium text-muted-foreground",children:"请求 Headers"}),e.jsx(V,{value:n.headers,onChange:a=>s(r,{headers:y(a)})})]})]}),e.jsxs("div",{className:"grid gap-3 md:grid-cols-2",children:[e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-medium text-muted-foreground",children:"HTTP 请求超时(秒)"}),e.jsx(g,{type:"number",min:.1,step:.1,value:n.http_timeout_seconds,onChange:a=>s(r,{http_timeout_seconds:Number.parseFloat(a.target.value)||.1})})]}),e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-medium text-muted-foreground",children:"会话读取超时(秒)"}),e.jsx(g,{type:"number",min:.1,step:.1,value:n.read_timeout_seconds,onChange:a=>s(r,{read_timeout_seconds:Number.parseFloat(a.target.value)||.1})})]})]})]})]},`${n.name}-${r}`))})]})}function Ve(){return e.jsx(ne,{children:e.jsx(ge,{})})}function ge(){const[t,i]=m.useState(!0),[s,c]=m.useState(!1),[d,u]=m.useState(!1),[x,n]=m.useState({}),[r,a]=m.useState(null),[p,F]=m.useState(()=>localStorage.getItem("mcp-settings-restart-notice-dismissed")!=="true"),{toast:h}=Y(),{triggerRestart:w,isRestarting:N}=oe();m.useEffect(()=>{const o=[["mcp.client.roots.items",ae]];for(const[l,f]of o)P.register(l,f,"replace");return()=>{for(const[l]of o)P.unregister(l)}},[]);const k=m.useCallback(async()=>{try{i(!0);const[o,l]=await Promise.all([q(),G()]);if(!o.success){h({title:"加载失败",description:o.error,variant:"destructive"});return}if(!l.success){h({title:"加载失败",description:l.error,variant:"destructive"});return}const f=o.data,_=f.config??f,M=l.data,X=M.schema??M;n(_.mcp??{}),a(X.nested?.mcp??null),u(!1)}catch(o){console.error("加载 MCP 设置失败:",o),h({title:"加载失败",description:o.message,variant:"destructive"})}finally{i(!1)}},[h]);m.useEffect(()=>{k()},[k]);const C=m.useCallback(async()=>{try{c(!0);const o=await J("mcp",x);return o.success?(u(!1),h({title:"保存成功",description:"MCP 设置已保存,重启后生效。"}),!0):(h({title:"保存失败",description:o.error,variant:"destructive"}),!1)}catch(o){return console.error("保存 MCP 设置失败:",o),h({title:"保存失败",description:o.message,variant:"destructive"}),!1}finally{c(!1)}},[x,h]),L=m.useCallback(async()=>{await C()&&await w({delay:500})},[C,w]),K=m.useCallback(()=>{localStorage.setItem("mcp-settings-restart-notice-dismissed","true"),F(!1)},[]),S=r?{className:"MCPSettings",classDoc:"MCP 设置",fields:[],nested:{mcp:{...r,fields:r.fields.filter(o=>o.name!=="servers"),nested:r.nested?Object.fromEntries(Object.entries(r.nested).filter(([o])=>o!=="servers")):void 0}}}:null,W=pe(x.servers);return e.jsx(Q,{className:"h-full",children:e.jsxs("div",{className:"space-y-4 sm:space-y-6 p-4 sm:p-6",children:[e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("h1",{className:"text-xl sm:text-2xl md:text-3xl font-bold",children:"MCP 设置"}),e.jsx("p",{className:"text-muted-foreground mt-1 text-xs sm:text-sm",children:"管理 MCP 客户端能力与服务器连接配置"})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(j,{onClick:C,disabled:t||s||!d||N,size:"sm",variant:"outline",className:"w-24",children:[e.jsx(ce,{className:"h-4 w-4",strokeWidth:2,fill:"none"}),e.jsx("span",{className:"ml-1 text-xs sm:text-sm",children:s?"保存中":d?"保存":"已保存"})]}),e.jsxs(j,{onClick:L,disabled:t||s||N,size:"sm",className:"w-28",children:[e.jsx(le,{className:"h-4 w-4"}),e.jsx("span",{className:"ml-1 text-xs sm:text-sm",children:N?"重启中":"保存重启"})]})]})]}),p&&e.jsxs(z,{children:[e.jsx(U,{className:"h-4 w-4"}),e.jsxs(A,{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[e.jsx("span",{children:"MCP 设置保存后需要重启麦麦才会生效。这里与主程序配置中的 MCP 栏目使用同一份配置。"}),e.jsx(j,{type:"button",variant:"outline",size:"sm",onClick:K,children:"我知道了"})]})]}),t&&e.jsx("div",{className:"flex h-64 items-center justify-center text-sm text-muted-foreground",children:"加载中..."}),!t&&e.jsx(fe,{servers:W,onChange:o=>{n(l=>({...l,servers:o})),u(!0)}}),!t&&S&&e.jsx(re,{schema:S,values:{mcp:x},onChange:(o,l)=>{const[,...f]=o.split("."),_=f.length===0?l:$(x,f,l);n(_),u(!0)},hooks:P}),!t&&!S&&e.jsxs(z,{children:[e.jsx(U,{className:"h-4 w-4"}),e.jsx(A,{children:"当前配置 schema 中没有找到 MCP 设置。"})]}),e.jsx(ie,{})]})})}export{Ve as MCPSettingsPage};