ellm-proxy 0.0.7 → 0.0.9

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 (53) hide show
  1. package/README.md +29 -11
  2. package/bin/ellm-proxy.js +2 -63
  3. package/dist/cli.js +35793 -0
  4. package/dist/server.js +16723 -6451
  5. package/dist/web-ui/assets/{DashboardView-BCwrFrE2.js → DashboardView-3c_i_3VF.js} +1 -1
  6. package/dist/web-ui/assets/LayoutView-BhN1f8wo.js +1 -0
  7. package/dist/web-ui/assets/LayoutView-CNd_MJt2.css +1 -0
  8. package/dist/web-ui/assets/{LoginView-Dp7NmXtf.js → LoginView-J23vsMu2.js} +1 -1
  9. package/dist/web-ui/assets/{LogsView-Dyy_zkgg.js → LogsView-2h2Y0XE8.js} +1 -1
  10. package/dist/web-ui/assets/ProcessView-Bn8Ph7rh.css +1 -0
  11. package/dist/web-ui/assets/ProcessView-Bs2k6aAf.js +1 -0
  12. package/dist/web-ui/assets/{ProvidersView-CYFs76wJ.js → ProvidersView-bE1Hv9D-.js} +1 -1
  13. package/dist/web-ui/assets/SettingsView-BmLmC-_v.js +1 -0
  14. package/dist/web-ui/assets/SettingsView-Ceyj-ZDZ.css +1 -0
  15. package/dist/web-ui/assets/{SetupView-DX6S_KpK.js → SetupView-Cgj7ubNz.js} +1 -1
  16. package/dist/web-ui/assets/{config-CRWaUFQc.js → config-nvSp8hHp.js} +1 -1
  17. package/dist/web-ui/assets/{index-X5PdkJlA.js → index-sPrBBnf5.js} +2 -2
  18. package/dist/web-ui/assets/{providers-H_9x778D.js → providers-di7L8jy_.js} +1 -1
  19. package/dist/web-ui/assets/useManagerApi-qPjxfB0X.js +1 -0
  20. package/dist/web-ui/assets/useToast-D76qyWZg.js +1 -0
  21. package/dist/web-ui/config.js +3 -6
  22. package/dist/web-ui/index.html +1 -1
  23. package/docs/DESIGN.md +43 -14
  24. package/package.json +2 -17
  25. package/bin/commands/autostart.js +0 -37
  26. package/bin/commands/config.js +0 -8
  27. package/bin/commands/credentials.js +0 -100
  28. package/bin/commands/pm2.js +0 -28
  29. package/bin/commands/service.js +0 -107
  30. package/dist/web-ui/assets/LayoutView-BUA8-IzK.css +0 -1
  31. package/dist/web-ui/assets/LayoutView-BePJfgia.js +0 -1
  32. package/dist/web-ui/assets/ProcessView-CpPpMtFg.js +0 -1
  33. package/dist/web-ui/assets/ProcessView-DK0TvDpR.css +0 -1
  34. package/dist/web-ui/assets/SettingsView-BkwyoOUb.css +0 -1
  35. package/dist/web-ui/assets/SettingsView-mPcaVAD2.js +0 -1
  36. package/dist/web-ui/assets/useManagerApi-C1_COxKg.js +0 -1
  37. package/dist/web-ui/assets/useToast-MtbkGJiM.js +0 -1
  38. package/manager/api/harness/backup.js +0 -20
  39. package/manager/api/harness/claude-code.js +0 -54
  40. package/manager/api/harness/codex.js +0 -123
  41. package/manager/api/harness/dsh.js +0 -175
  42. package/manager/api/harness/index.js +0 -118
  43. package/manager/api/service-settings.js +0 -98
  44. package/manager/api/service.js +0 -147
  45. package/manager/index.js +0 -182
  46. package/utils/autostart.js +0 -219
  47. package/utils/interpreter.js +0 -14
  48. package/utils/paths.js +0 -45
  49. package/utils/pm2.js +0 -130
  50. package/utils/port.js +0 -35
  51. package/utils/service.js +0 -205
  52. package/utils/ui.js +0 -59
  53. package/utils/updater.js +0 -233
@@ -1 +1 @@
1
- import{d as H,p as J,e as h,g as o,w as s,c as V,i as B,r as y,A as T,a as _,j as Q,o as c,b as W,k as Y,t as u,f as l,h as p,F as X,q as Z,l as N,_ as ee}from"./index-X5PdkJlA.js";import{u as te}from"./providers-H_9x778D.js";import{u as ae}from"./useToast-MtbkGJiM.js";import"./config-CRWaUFQc.js";const le={class:"flex-col-gap"},oe=["onClick"],se={key:1},ne={class:"flex-between"},de={class:"flex-gap-8"},ue={class:"usage-grid"},ie={class:"usage-cell"},re={class:"usage-num orange"},ce={class:"usage-cell"},ve={class:"usage-num blue"},_e={class:"usage-cell"},pe={class:"usage-num green"},ge={class:"usage-cell"},me={class:"usage-num purple"},fe={class:"usage-cell"},he={class:"usage-num purple"},ye={key:0,class:"usage-cell"},be={class:"usage-num gray"},we={key:0},ke=["onClick"],De=H({__name:"DashboardView",setup(Ce){const k=te(),{toast:M}=ae(),i=y(null),b=y(!0),D=y(""),v=y(null),w=y("today"),C=a=>{const e=a.getFullYear(),t=String(a.getMonth()+1).padStart(2,"0"),r=String(a.getDate()).padStart(2,"0");return`${e}-${t}-${r}`},F=a=>{const e=new Date;let t,r=e;if(a==="today")t=e;else if(a==="yesterday")t=new Date(e),t.setDate(e.getDate()-1),r=t;else if(a==="beforeYesterday")t=new Date(e),t.setDate(e.getDate()-2),r=t;else if(a==="last3days")t=new Date(e),t.setDate(e.getDate()-2);else if(a==="week"){t=new Date(e);const S=(e.getDay()+6)%7;t.setDate(e.getDate()-S)}else if(a==="month")t=new Date(e.getFullYear(),e.getMonth(),1);else return;w.value=a,v.value=[C(t),C(r)],f()},U=a=>{a?F(a):(w.value="",v.value=null,f())},A=()=>{w.value="",f()},I=N(()=>{var e;const a=new Set;for(const t of k.suppliers)for(const r of t.models)a.add(r.modelName);if((e=i.value)!=null&&e.models)for(const t of i.value.models)a.add(t.model);return[...a].sort()}),f=async()=>{var a;b.value=!0;try{const e=new URLSearchParams;D.value&&e.set("model",D.value),((a=v.value)==null?void 0:a.length)===2&&(e.set("startDate",v.value[0]),e.set("endDate",v.value[1]));const t=e.toString();i.value=await T.get(`/api/usage${t?`?${t}`:""}`)}catch(e){console.error(e)}finally{b.value=!1}},R=async()=>{var a;try{const[e]=await Promise.all([T.get("/api/usage"),k.loadProviders(),T.get("/api/status").then(t=>x.value=t.listenPort).catch(()=>x.value)]);if(i.value=e,!((a=v.value)!=null&&a.length)){const t=new Date;v.value=[C(t),C(t)],await f()}}catch(e){console.error(e)}finally{b.value=!1}},L=window.location.hostname,x=y(Number(window.location.port)||null),q=N(()=>`http://${L}${x.value?`:${x.value}`:""}`),g=a=>a.toLocaleString(),E=a=>a==null||a<=0?"-":a>=1e3?(a/1e3).toFixed(2)+"s":Math.round(a)+"ms",$=async a=>{try{await navigator.clipboard.writeText(a),M("已复制到剪贴板","success")}catch{const e=document.createElement("textarea");e.value=a,e.style.position="fixed",e.style.opacity="0",document.body.appendChild(e),e.select();try{document.execCommand("copy"),M("已复制到剪贴板","success")}catch{M("复制失败","error")}document.body.removeChild(e)}};return J(R),(a,e)=>{const t=_("el-table-column"),r=_("el-table"),S=_("el-card"),m=_("el-option"),P=_("el-select"),j=_("el-date-picker"),z=_("el-button"),K=_("el-tag"),O=_("el-empty"),G=Q("loading");return c(),h("div",le,[o(S,{shadow:"never"},{header:s(()=>[...e[3]||(e[3]=[l("span",{class:"fw-600"},"服务信息",-1)])]),default:s(()=>[W((c(),V(r,{border:"",data:[{key:"API 地址",value:q.value},{key:"供应商数量",value:String(Y(k).providerCount)},{key:"模型总数",value:String(Y(k).totalModelCount)}],"show-header":!1,class:"full-width"},{default:s(()=>[o(t,{prop:"key",width:"160"}),o(t,null,{default:s(({row:d})=>[d.key==="API 地址"?(c(),h("span",{key:0,class:"copy-text",onClick:n=>$(d.value)},u(d.value),9,oe)):(c(),h("span",se,u(d.value),1))]),_:1})]),_:1},8,["data"])),[[G,b.value]])]),_:1}),i.value?(c(),V(S,{key:0,shadow:"never"},{header:s(()=>[l("div",ne,[e[5]||(e[5]=l("span",{class:"fw-600"},"用量统计",-1)),l("div",de,[o(P,{modelValue:D.value,"onUpdate:modelValue":e[0]||(e[0]=d=>D.value=d),placeholder:"全部模型",clearable:"",filterable:"",style:{width:"220px"},onChange:f},{default:s(()=>[(c(!0),h(X,null,Z(I.value,d=>(c(),V(m,{key:d,label:d,value:d},null,8,["label","value"]))),128))]),_:1},8,["modelValue"]),o(P,{modelValue:w.value,"onUpdate:modelValue":e[1]||(e[1]=d=>w.value=d),placeholder:"时间范围",clearable:"",style:{width:"130px"},onChange:U},{default:s(()=>[o(m,{label:"今天",value:"today"}),o(m,{label:"昨天",value:"yesterday"}),o(m,{label:"前天",value:"beforeYesterday"}),o(m,{label:"近三天",value:"last3days"}),o(m,{label:"本周",value:"week"}),o(m,{label:"本月",value:"month"})]),_:1},8,["modelValue"]),o(j,{modelValue:v.value,"onUpdate:modelValue":e[2]||(e[2]=d=>v.value=d),type:"daterange","range-separator":"至","start-placeholder":"开始日期","end-placeholder":"结束日期","value-format":"YYYY-MM-DD",style:{width:"260px"},onChange:A},null,8,["modelValue"]),o(z,{loading:b.value,onClick:f},{default:s(()=>[...e[4]||(e[4]=[p("刷新",-1)])]),_:1},8,["loading"])]),o(K,{type:"info"},{default:s(()=>[p(u(i.value.total.calls)+" 次调用",1)]),_:1})])]),default:s(()=>{var d;return[l("div",ue,[l("div",ie,[l("div",re,u(g(i.value.total.total_tokens)),1),e[6]||(e[6]=l("div",{class:"usage-label"},"总 Token",-1))]),l("div",ce,[l("div",ve,u(g(i.value.total.input_tokens)),1),e[7]||(e[7]=l("div",{class:"usage-label"},"输入 Token",-1))]),l("div",_e,[l("div",pe,u(g(i.value.total.output_tokens)),1),e[8]||(e[8]=l("div",{class:"usage-label"},"输出 Token",-1))]),l("div",ge,[l("div",me,u(g(i.value.total.cache_read_tokens)),1),e[9]||(e[9]=l("div",{class:"usage-label"},"缓存读取",-1))]),l("div",fe,[l("div",he,u(i.value.total.cache_rate!=null?i.value.total.cache_rate+"%":"-"),1),e[10]||(e[10]=l("div",{class:"usage-label"},"缓存命中率",-1))]),i.value.logStorageMB!=null?(c(),h("div",ye,[l("div",be,u(i.value.logStorageMB),1),e[11]||(e[11]=l("div",{class:"usage-label"},"日志占用 (MB)",-1))])):B("",!0)]),(d=i.value.models)!=null&&d.length?(c(),h("div",we,[e[12]||(e[12]=l("div",{class:"section-title"},"模型用量",-1)),o(r,{data:i.value.models,border:"",stripe:"",class:"full-width"},{default:s(()=>[o(t,{prop:"model",label:"模型","min-width":"200"},{default:s(({row:n})=>[l("span",{class:"copy-text",onClick:xe=>$(n.model)},u(n.model),9,ke)]),_:1}),o(t,{label:"调用次数",width:"90",align:"center"},{default:s(({row:n})=>[p(u(n.calls),1)]),_:1}),o(t,{label:"平均耗时",width:"110",align:"center"},{default:s(({row:n})=>[p(u(E(n.avg_duration)),1)]),_:1}),o(t,{label:"总 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.total_tokens)),1)]),_:1}),o(t,{label:"输入 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.input_tokens)),1)]),_:1}),o(t,{label:"输出 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.output_tokens)),1)]),_:1}),o(t,{label:"缓存读取",width:"130",align:"center"},{default:s(({row:n})=>[p(u(g(n.cache_read_tokens)),1)]),_:1}),o(t,{label:"缓存命中率",width:"110",align:"center"},{default:s(({row:n})=>[p(u(n.cache_rate!=null?n.cache_rate+"%":"-"),1)]),_:1})]),_:1},8,["data"])])):(c(),V(O,{key:1,description:"暂无调用记录","image-size":60}))]}),_:1})):B("",!0)])}}}),$e=ee(De,[["__scopeId","data-v-de31ace5"]]);export{$e as default};
1
+ import{d as H,p as J,e as h,g as o,w as s,c as V,i as B,r as y,A as T,a as _,j as Q,o as c,b as W,k as Y,t as u,f as l,h as p,F as X,q as Z,l as N,_ as ee}from"./index-sPrBBnf5.js";import{u as te}from"./providers-di7L8jy_.js";import{u as ae}from"./useToast-D76qyWZg.js";import"./config-nvSp8hHp.js";const le={class:"flex-col-gap"},oe=["onClick"],se={key:1},ne={class:"flex-between"},de={class:"flex-gap-8"},ue={class:"usage-grid"},ie={class:"usage-cell"},re={class:"usage-num orange"},ce={class:"usage-cell"},ve={class:"usage-num blue"},_e={class:"usage-cell"},pe={class:"usage-num green"},ge={class:"usage-cell"},me={class:"usage-num purple"},fe={class:"usage-cell"},he={class:"usage-num purple"},ye={key:0,class:"usage-cell"},be={class:"usage-num gray"},we={key:0},ke=["onClick"],De=H({__name:"DashboardView",setup(Ce){const k=te(),{toast:M}=ae(),i=y(null),b=y(!0),D=y(""),v=y(null),w=y("today"),C=a=>{const e=a.getFullYear(),t=String(a.getMonth()+1).padStart(2,"0"),r=String(a.getDate()).padStart(2,"0");return`${e}-${t}-${r}`},F=a=>{const e=new Date;let t,r=e;if(a==="today")t=e;else if(a==="yesterday")t=new Date(e),t.setDate(e.getDate()-1),r=t;else if(a==="beforeYesterday")t=new Date(e),t.setDate(e.getDate()-2),r=t;else if(a==="last3days")t=new Date(e),t.setDate(e.getDate()-2);else if(a==="week"){t=new Date(e);const S=(e.getDay()+6)%7;t.setDate(e.getDate()-S)}else if(a==="month")t=new Date(e.getFullYear(),e.getMonth(),1);else return;w.value=a,v.value=[C(t),C(r)],f()},U=a=>{a?F(a):(w.value="",v.value=null,f())},A=()=>{w.value="",f()},I=N(()=>{var e;const a=new Set;for(const t of k.suppliers)for(const r of t.models)a.add(r.modelName);if((e=i.value)!=null&&e.models)for(const t of i.value.models)a.add(t.model);return[...a].sort()}),f=async()=>{var a;b.value=!0;try{const e=new URLSearchParams;D.value&&e.set("model",D.value),((a=v.value)==null?void 0:a.length)===2&&(e.set("startDate",v.value[0]),e.set("endDate",v.value[1]));const t=e.toString();i.value=await T.get(`/api/usage${t?`?${t}`:""}`)}catch(e){console.error(e)}finally{b.value=!1}},R=async()=>{var a;try{const[e]=await Promise.all([T.get("/api/usage"),k.loadProviders(),T.get("/api/status").then(t=>x.value=t.listenPort).catch(()=>x.value)]);if(i.value=e,!((a=v.value)!=null&&a.length)){const t=new Date;v.value=[C(t),C(t)],await f()}}catch(e){console.error(e)}finally{b.value=!1}},L=window.location.hostname,x=y(Number(window.location.port)||null),q=N(()=>`http://${L}${x.value?`:${x.value}`:""}`),g=a=>a.toLocaleString(),E=a=>a==null||a<=0?"-":a>=1e3?(a/1e3).toFixed(2)+"s":Math.round(a)+"ms",$=async a=>{try{await navigator.clipboard.writeText(a),M("已复制到剪贴板","success")}catch{const e=document.createElement("textarea");e.value=a,e.style.position="fixed",e.style.opacity="0",document.body.appendChild(e),e.select();try{document.execCommand("copy"),M("已复制到剪贴板","success")}catch{M("复制失败","error")}document.body.removeChild(e)}};return J(R),(a,e)=>{const t=_("el-table-column"),r=_("el-table"),S=_("el-card"),m=_("el-option"),P=_("el-select"),j=_("el-date-picker"),z=_("el-button"),K=_("el-tag"),O=_("el-empty"),G=Q("loading");return c(),h("div",le,[o(S,{shadow:"never"},{header:s(()=>[...e[3]||(e[3]=[l("span",{class:"fw-600"},"服务信息",-1)])]),default:s(()=>[W((c(),V(r,{border:"",data:[{key:"API 地址",value:q.value},{key:"供应商数量",value:String(Y(k).providerCount)},{key:"模型总数",value:String(Y(k).totalModelCount)}],"show-header":!1,class:"full-width"},{default:s(()=>[o(t,{prop:"key",width:"160"}),o(t,null,{default:s(({row:d})=>[d.key==="API 地址"?(c(),h("span",{key:0,class:"copy-text",onClick:n=>$(d.value)},u(d.value),9,oe)):(c(),h("span",se,u(d.value),1))]),_:1})]),_:1},8,["data"])),[[G,b.value]])]),_:1}),i.value?(c(),V(S,{key:0,shadow:"never"},{header:s(()=>[l("div",ne,[e[5]||(e[5]=l("span",{class:"fw-600"},"用量统计",-1)),l("div",de,[o(P,{modelValue:D.value,"onUpdate:modelValue":e[0]||(e[0]=d=>D.value=d),placeholder:"全部模型",clearable:"",filterable:"",style:{width:"220px"},onChange:f},{default:s(()=>[(c(!0),h(X,null,Z(I.value,d=>(c(),V(m,{key:d,label:d,value:d},null,8,["label","value"]))),128))]),_:1},8,["modelValue"]),o(P,{modelValue:w.value,"onUpdate:modelValue":e[1]||(e[1]=d=>w.value=d),placeholder:"时间范围",clearable:"",style:{width:"130px"},onChange:U},{default:s(()=>[o(m,{label:"今天",value:"today"}),o(m,{label:"昨天",value:"yesterday"}),o(m,{label:"前天",value:"beforeYesterday"}),o(m,{label:"近三天",value:"last3days"}),o(m,{label:"本周",value:"week"}),o(m,{label:"本月",value:"month"})]),_:1},8,["modelValue"]),o(j,{modelValue:v.value,"onUpdate:modelValue":e[2]||(e[2]=d=>v.value=d),type:"daterange","range-separator":"至","start-placeholder":"开始日期","end-placeholder":"结束日期","value-format":"YYYY-MM-DD",style:{width:"260px"},onChange:A},null,8,["modelValue"]),o(z,{loading:b.value,onClick:f},{default:s(()=>[...e[4]||(e[4]=[p("刷新",-1)])]),_:1},8,["loading"])]),o(K,{type:"info"},{default:s(()=>[p(u(i.value.total.calls)+" 次调用",1)]),_:1})])]),default:s(()=>{var d;return[l("div",ue,[l("div",ie,[l("div",re,u(g(i.value.total.total_tokens)),1),e[6]||(e[6]=l("div",{class:"usage-label"},"总 Token",-1))]),l("div",ce,[l("div",ve,u(g(i.value.total.input_tokens)),1),e[7]||(e[7]=l("div",{class:"usage-label"},"输入 Token",-1))]),l("div",_e,[l("div",pe,u(g(i.value.total.output_tokens)),1),e[8]||(e[8]=l("div",{class:"usage-label"},"输出 Token",-1))]),l("div",ge,[l("div",me,u(g(i.value.total.cache_read_tokens)),1),e[9]||(e[9]=l("div",{class:"usage-label"},"缓存读取",-1))]),l("div",fe,[l("div",he,u(i.value.total.cache_rate!=null?i.value.total.cache_rate+"%":"-"),1),e[10]||(e[10]=l("div",{class:"usage-label"},"缓存命中率",-1))]),i.value.logStorageMB!=null?(c(),h("div",ye,[l("div",be,u(i.value.logStorageMB),1),e[11]||(e[11]=l("div",{class:"usage-label"},"日志占用 (MB)",-1))])):B("",!0)]),(d=i.value.models)!=null&&d.length?(c(),h("div",we,[e[12]||(e[12]=l("div",{class:"section-title"},"模型用量",-1)),o(r,{data:i.value.models,border:"",stripe:"",class:"full-width"},{default:s(()=>[o(t,{prop:"model",label:"模型","min-width":"200"},{default:s(({row:n})=>[l("span",{class:"copy-text",onClick:xe=>$(n.model)},u(n.model),9,ke)]),_:1}),o(t,{label:"调用次数",width:"90",align:"center"},{default:s(({row:n})=>[p(u(n.calls),1)]),_:1}),o(t,{label:"平均耗时",width:"110",align:"center"},{default:s(({row:n})=>[p(u(E(n.avg_duration)),1)]),_:1}),o(t,{label:"总 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.total_tokens)),1)]),_:1}),o(t,{label:"输入 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.input_tokens)),1)]),_:1}),o(t,{label:"输出 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.output_tokens)),1)]),_:1}),o(t,{label:"缓存读取",width:"130",align:"center"},{default:s(({row:n})=>[p(u(g(n.cache_read_tokens)),1)]),_:1}),o(t,{label:"缓存命中率",width:"110",align:"center"},{default:s(({row:n})=>[p(u(n.cache_rate!=null?n.cache_rate+"%":"-"),1)]),_:1})]),_:1},8,["data"])])):(c(),V(O,{key:1,description:"暂无调用记录","image-size":60}))]}),_:1})):B("",!0)])}}}),$e=ee(De,[["__scopeId","data-v-de31ace5"]]);export{$e as default};
@@ -0,0 +1 @@
1
+ import{d as E,u as N,c as b,w as e,r as w,a,o as f,b as W,e as D,F as z,f as p,g as o,h as i,t as x,i as C,E as F,j as I,_ as L,k as S,l as R,m as j,n as O}from"./index-sPrBBnf5.js";import{m as A,w as q}from"./useManagerApi-qPjxfB0X.js";import{u as G}from"./useToast-D76qyWZg.js";const H={class:"check-body"},J={class:"version-row"},K={class:"version-row"},P=E({__name:"UpdateCheckDialog",props:{modelValue:{type:Boolean,default:!1},modelModifiers:{}},emits:["update:modelValue"],setup(B){const{toast:V}=G(),y=N(B,"modelValue"),m=w(!1),v=w(!1),n=w(null),g=w(null),u=w(null),r=async()=>{n.value=null,g.value=null,u.value=null,m.value=!0;try{n.value=await A.checkUpdate()}catch(d){g.value=d.message||"检查更新失败"}finally{m.value=!1}},t=async()=>{var d;if(n.value){try{await F.confirm(`确定更新到 ${n.value.latestVersion}?服务将自动重启并在启动时安装新版本(期间界面请求会短暂失败,属正常现象)。`,"更新确认",{confirmButtonText:"立即更新",cancelButtonText:"取消",type:"warning"})}catch{return}v.value=!0;try{const l=await A.update(),c=await A.applyUpdate();if(!c.updated){u.value=`已是最新版本 ${c.toVersion}`,n.value={hasUpdate:!1,currentVersion:c.toVersion,latestVersion:c.toVersion,checkedAt:Date.now()};return}u.value=`正在重启服务并安装 ${c.toVersion},请稍候(预计 1~3 分钟)...`;const s=await q({targetVersion:c.toVersion,sinceAppliedAt:((d=l.lastApply)==null?void 0:d.appliedAt)??null});s&&!s.error?(V(`已更新到 ${s.toVersion}`,"success"),u.value=`已更新 ${s.fromVersion} → ${s.toVersion}`,n.value={hasUpdate:!1,currentVersion:s.toVersion,latestVersion:s.toVersion,checkedAt:Date.now()}):s!=null&&s.error?(V(`更新失败:${s.error}`,"error"),u.value=`更新失败:${s.error}(服务已恢复旧版本)`):u.value="更新耗时较长,请稍后刷新页面查看结果"}catch(l){V(l.message||"更新失败","error")}finally{v.value=!1}}};return(d,l)=>{const c=a("el-tag"),s=a("el-alert"),k=a("el-button"),h=a("el-empty"),$=a("el-dialog"),U=I("loading");return f(),b($,{modelValue:y.value,"onUpdate:modelValue":l[2]||(l[2]=_=>y.value=_),title:"检查更新",width:"420px","close-on-click-modal":!v.value,onOpen:r},{footer:e(()=>{var _;return[(_=n.value)!=null&&_.hasUpdate?(f(),b(k,{key:0,type:"primary",loading:v.value,onClick:t},{default:e(()=>[i(x(v.value?"更新中...":"立即更新"),1)]),_:1},8,["loading"])):C("",!0),o(k,{onClick:l[1]||(l[1]=T=>y.value=!1)},{default:e(()=>[...l[6]||(l[6]=[i("关闭",-1)])]),_:1})]}),default:e(()=>[W((f(),D("div",H,[n.value?(f(),D(z,{key:0},[p("div",J,[l[3]||(l[3]=p("span",{class:"version-label"},"当前版本",-1)),o(c,{size:"small"},{default:e(()=>[i(x(n.value.currentVersion),1)]),_:1})]),p("div",K,[l[4]||(l[4]=p("span",{class:"version-label"},"最新版本",-1)),o(c,{type:n.value.hasUpdate?"warning":"success",size:"small","disable-transitions":""},{default:e(()=>[i(x(n.value.latestVersion),1)]),_:1},8,["type"])]),o(s,{title:n.value.hasUpdate?`发现新版本 ${n.value.latestVersion}`:"已是最新版本",type:n.value.hasUpdate?"warning":"success","show-icon":"",closable:!1,class:"mt-12"},null,8,["title","type"])],64)):!m.value&&g.value?(f(),b(h,{key:1,description:g.value},{default:e(()=>[o(k,{type:"primary",onClick:r},{default:e(()=>[...l[5]||(l[5]=[i("重试",-1)])]),_:1})]),_:1},8,["description"])):C("",!0)])),[[U,m.value]]),u.value?(f(),b(s,{key:0,title:u.value,type:"info","show-icon":"",class:"mt-12",onClose:l[0]||(l[0]=_=>u.value=null)},null,8,["title"])):C("",!0)]),_:1},8,["modelValue","close-on-click-modal"])}}}),Q=L(P,[["__scopeId","data-v-eaed444e"]]),X={class:"layout"},Y={class:"header-nav"},Z={class:"header-actions"},ee={class:"main"},te={class:"page"},ne=E({__name:"LayoutView",setup(B){const V=O(),y=j(),m=w(!1),v=(()=>{const r=localStorage.getItem("webui_token")||"";try{return atob(r).split(":")[0]||"用户"}catch{return"用户"}})(),n=R(()=>{const r=V.path;return r==="/"?"/":r.startsWith("/providers")?"/providers":r.startsWith("/settings")?"/settings":r.startsWith("/logs")?"/logs":r.startsWith("/process")?"/process":"/"}),g=()=>{localStorage.removeItem("webui_token"),y.push("/login")},u=r=>{r==="logout"&&g(),r==="check-update"&&(m.value=!0)};return(r,t)=>{const d=a("el-menu-item"),l=a("el-menu"),c=a("ArrowDown"),s=a("el-icon"),k=a("el-button"),h=a("el-dropdown-item"),$=a("el-dropdown-menu"),U=a("el-dropdown"),_=a("el-header"),T=a("router-view");return f(),D("div",X,[o(_,{class:"header"},{default:e(()=>[t[8]||(t[8]=p("h1",null,[p("span",null,"E"),i("LLM")],-1)),p("nav",Y,[o(l,{mode:"horizontal","default-active":n.value,ellipsis:!1,router:"",class:"header-menu"},{default:e(()=>[o(d,{index:"/"},{default:e(()=>[...t[1]||(t[1]=[i("首页",-1)])]),_:1}),o(d,{index:"/providers"},{default:e(()=>[...t[2]||(t[2]=[i("供应商管理",-1)])]),_:1}),o(d,{index:"/logs"},{default:e(()=>[...t[3]||(t[3]=[i("日志",-1)])]),_:1}),o(d,{index:"/settings"},{default:e(()=>[...t[4]||(t[4]=[i("设置",-1)])]),_:1}),o(d,{index:"/process"},{default:e(()=>[...t[5]||(t[5]=[i("进程管理",-1)])]),_:1})]),_:1},8,["default-active"])]),p("div",Z,[o(U,{trigger:"click",onCommand:u,class:"ml-12"},{dropdown:e(()=>[o($,null,{default:e(()=>[o(h,{command:"check-update"},{default:e(()=>[...t[6]||(t[6]=[i("检查更新",-1)])]),_:1}),o(h,{command:"logout",divided:""},{default:e(()=>[...t[7]||(t[7]=[i("退出",-1)])]),_:1})]),_:1})]),default:e(()=>[o(k,null,{default:e(()=>[i(x(S(v))+" ",1),o(s,{class:"el-icon--right"},{default:e(()=>[o(c)]),_:1})]),_:1})]),_:1})])]),_:1}),p("main",ee,[p("div",te,[o(T)])]),o(Q,{modelValue:m.value,"onUpdate:modelValue":t[0]||(t[0]=M=>m.value=M)},null,8,["modelValue"])])}}});export{ne as default};
@@ -0,0 +1 @@
1
+ .mt-12[data-v-eaed444e]{margin-top:12px}.check-body[data-v-eaed444e]{min-height:80px}.version-row[data-v-eaed444e]{display:flex;align-items:center;gap:8px;margin-bottom:8px}.version-label[data-v-eaed444e]{color:var(--el-text-color-secondary);font-size:13px}
@@ -1 +1 @@
1
- import{d as h,e as b,g as o,w as l,a,o as _,f as k,Z as x,a3 as f,c as C,i as L,h as B,r as i,A as N,m as I,_ as K}from"./index-X5PdkJlA.js";const A={class:"login-wrap"},E=h({__name:"LoginView",setup(M){const v=I(),n=i(""),s=i(""),t=i(""),d=i(!1);async function r(){if(!n.value){t.value="请输入用户名";return}if(!s.value){t.value="请输入密码";return}d.value=!0,t.value="";try{const u=await N.post("/api/auth/login",{username:n.value.trim(),password:s.value});localStorage.setItem("webui_token",u.token),v.push("/")}catch(u){t.value=u.message||"登录失败"}finally{d.value=!1}}return(u,e)=>{const p=a("el-input"),m=a("el-form-item"),w=a("el-alert"),g=a("el-button"),V=a("el-form"),y=a("el-card");return _(),b("div",A,[o(y,{shadow:"hover",class:"login-card"},{default:l(()=>[e[3]||(e[3]=k("h2",{class:"login-title"},"ELLM",-1)),o(V,{"label-position":"top",onSubmit:x(r,["prevent"])},{default:l(()=>[o(m,{label:"用户名"},{default:l(()=>[o(p,{modelValue:n.value,"onUpdate:modelValue":e[0]||(e[0]=c=>n.value=c),placeholder:"请输入用户名",onKeyup:f(r,["enter"])},null,8,["modelValue"])]),_:1}),o(m,{label:"密码"},{default:l(()=>[o(p,{modelValue:s.value,"onUpdate:modelValue":e[1]||(e[1]=c=>s.value=c),type:"password",placeholder:"请输入密码","show-password":"",onKeyup:f(r,["enter"])},null,8,["modelValue"])]),_:1}),t.value?(_(),C(w,{key:0,title:t.value,type:"error","show-icon":"",class:"mb-16"},null,8,["title"])):L("",!0),o(g,{type:"primary",class:"full-width",loading:d.value,onClick:r},{default:l(()=>[...e[2]||(e[2]=[B(" 登录 ",-1)])]),_:1},8,["loading"])]),_:1})]),_:1})])}}}),U=K(E,[["__scopeId","data-v-c944978e"]]);export{U as default};
1
+ import{d as h,e as b,g as o,w as l,a,o as _,f as k,Z as x,a3 as f,c as C,i as L,h as B,r as i,A as N,m as I,_ as K}from"./index-sPrBBnf5.js";const A={class:"login-wrap"},E=h({__name:"LoginView",setup(M){const v=I(),n=i(""),s=i(""),t=i(""),d=i(!1);async function r(){if(!n.value){t.value="请输入用户名";return}if(!s.value){t.value="请输入密码";return}d.value=!0,t.value="";try{const u=await N.post("/api/auth/login",{username:n.value.trim(),password:s.value});localStorage.setItem("webui_token",u.token),v.push("/")}catch(u){t.value=u.message||"登录失败"}finally{d.value=!1}}return(u,e)=>{const p=a("el-input"),m=a("el-form-item"),w=a("el-alert"),g=a("el-button"),V=a("el-form"),y=a("el-card");return _(),b("div",A,[o(y,{shadow:"hover",class:"login-card"},{default:l(()=>[e[3]||(e[3]=k("h2",{class:"login-title"},"ELLM",-1)),o(V,{"label-position":"top",onSubmit:x(r,["prevent"])},{default:l(()=>[o(m,{label:"用户名"},{default:l(()=>[o(p,{modelValue:n.value,"onUpdate:modelValue":e[0]||(e[0]=c=>n.value=c),placeholder:"请输入用户名",onKeyup:f(r,["enter"])},null,8,["modelValue"])]),_:1}),o(m,{label:"密码"},{default:l(()=>[o(p,{modelValue:s.value,"onUpdate:modelValue":e[1]||(e[1]=c=>s.value=c),type:"password",placeholder:"请输入密码","show-password":"",onKeyup:f(r,["enter"])},null,8,["modelValue"])]),_:1}),t.value?(_(),C(w,{key:0,title:t.value,type:"error","show-icon":"",class:"mb-16"},null,8,["title"])):L("",!0),o(g,{type:"primary",class:"full-width",loading:d.value,onClick:r},{default:l(()=>[...e[2]||(e[2]=[B(" 登录 ",-1)])]),_:1},8,["loading"])]),_:1})]),_:1})])}}}),U=K(E,[["__scopeId","data-v-c944978e"]]);export{U as default};
@@ -1,4 +1,4 @@
1
- import{d as G,a as b,j as H,o as _,c as q,w as a,e as g,g as e,h as i,t as u,k as m,b as Q,F as P,f as d,i as j,r as y,A,_ as X,p as pe,q as ce,a3 as _e,l as ve}from"./index-X5PdkJlA.js";import{u as me}from"./providers-H_9x778D.js";import{u as Z}from"./useToast-MtbkGJiM.js";import"./config-CRWaUFQc.js";async function ee(p){try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(p),!0}catch{}try{const f=document.createElement("textarea");f.value=p,f.style.position="fixed",f.style.opacity="0",document.body.appendChild(f),f.select();const k=document.execCommand("copy");return document.body.removeChild(f),k}catch{return!1}}const te=p=>{if(!p)return"-";const f=new Date(p),k=w=>String(w).padStart(2,"0");return`${f.getFullYear()}-${k(f.getMonth()+1)}-${k(f.getDate())} ${k(f.getHours())}:${k(f.getMinutes())}:${k(f.getSeconds())}`},V=p=>p?typeof p=="string"?p:JSON.stringify(p,null,2):"-",fe=p=>{if(!p)return"-";try{return JSON.stringify(JSON.parse(p),null,2)}catch{return p}},T=p=>p.toLocaleString(),le=p=>p==null||p<=0?"-":p>=1e3?(p/1e3).toFixed(2)+"s":Math.round(p)+"ms",ae=p=>{const f=p.input_tokens||0,k=p.cache_read_tokens||0;return f<=0?"-":(k/f*100).toFixed(1)+"%"},be={key:0,class:"detail-content"},ge={key:0,class:"ip-link"},ye={class:"ip-pop"},ke={class:"ip-pop-row"},he={key:0,class:"ip-pop-row"},xe={key:1,class:"ip-pop-error"},we={key:2},De=["href"],Ce={key:1},$e={key:1},Ve={class:"error-text"},Se={class:"pane-bar"},Ie={class:"json-block"},qe={class:"pane-bar"},ze={class:"json-block"},Re={class:"pane-bar"},Ue={class:"json-block"},je={class:"pane-bar"},Te={class:"json-block"},Ne={class:"pane-bar"},Le={class:"json-block"},Me={class:"pane-bar"},Ye={class:"json-block"},Ee={class:"pane-bar"},Pe={class:"json-block"},Fe={class:"pane-bar"},Be={class:"json-block"},Je=G({__name:"LogDetailDialog",setup(p,{expose:f}){const{toast:k}=Z(),w=y(!1),t=y(null),S=y("info"),I=y(!1);f({open:async v=>{w.value=!0,t.value=null,S.value="info";try{const l=v.id?`?id=${v.id}`:`?_file=${encodeURIComponent(v._file)}`,c=await A.get("/api/logs/detail"+l);c&&c.detail?t.value={...c.detail,...c.meta}:t.value=c}catch(l){console.error(l)}}});const h=async v=>{await ee(v)?k("已复制到剪贴板","success"):k("复制失败","error")},N=async v=>{if(!(v._ipInfo||v._ipInfoError)){I.value=!0;try{const l=await A.get(`/api/logs/ipinfo?ip=${encodeURIComponent(v.ip)}`);l.error?v._ipInfoError=l.error:v._ipInfo=l}catch{v._ipInfoError="查询失败"}finally{I.value=!1}}},M=v=>[v.country,v.region,v.city].filter(Boolean).join(" · ")||"-";return(v,l)=>{const c=b("el-descriptions-item"),Y=b("el-tag"),F=b("el-divider"),B=b("el-popover"),J=b("el-descriptions"),E=b("el-alert"),x=b("el-tab-pane"),D=b("el-button"),K=b("el-tabs"),L=b("el-dialog"),C=H("loading");return _(),q(L,{modelValue:w.value,"onUpdate:modelValue":l[11]||(l[11]=z=>w.value=z),title:"日志详情",width:"80%",top:"5vh","destroy-on-close":""},{default:a(()=>[t.value?(_(),g("div",be,[e(K,{modelValue:S.value,"onUpdate:modelValue":l[10]||(l[10]=z=>S.value=z)},{default:a(()=>{var z,r,s;return[e(x,{label:"基本信息",name:"info"},{default:a(()=>{var n;return[e(J,{column:2,border:""},{default:a(()=>[e(c,{label:"时间"},{default:a(()=>[i(u(m(te)(t.value.time)),1)]),_:1}),e(c,{label:"模型"},{default:a(()=>[i(u(t.value.model),1)]),_:1}),e(c,{label:"方法"},{default:a(()=>[i(u(t.value.method),1)]),_:1}),e(c,{label:"路径"},{default:a(()=>[i(u(t.value.path),1)]),_:1}),e(c,{label:"状态码"},{default:a(()=>[i(u(t.value.status),1)]),_:1}),e(c,{label:"流式"},{default:a(()=>[e(Y,{type:t.value.stream?"warning":"info"},{default:a(()=>[i(u(t.value.stream?"是":"否"),1)]),_:1},8,["type"])]),_:1}),e(c,{label:"IP"},{default:a(()=>[t.value.ip?(_(),g("span",ge,[e(B,{placement:"right",width:"240",trigger:"click",onShow:l[0]||(l[0]=R=>N(t.value))},{reference:a(()=>[d("span",null,u(t.value.ip),1)]),default:a(()=>[Q((_(),g("div",ye,[t.value._ipInfo?(_(),g(P,{key:0},[d("div",ke,[l[12]||(l[12]=d("span",{class:"ip-pop-label"},"归属地",-1)),d("span",null,u(M(t.value._ipInfo)),1)]),t.value._ipInfo.isp?(_(),g("div",he,[l[13]||(l[13]=d("span",{class:"ip-pop-label"},"运营商",-1)),d("span",null,u(t.value._ipInfo.isp),1)])):j("",!0)],64)):t.value._ipInfoError?(_(),g("div",xe,u(t.value._ipInfoError),1)):(_(),g("span",we,"查询中...")),e(F,{style:{margin:"8px 0"}}),d("a",{class:"ip-pop-link",href:`https://m.ip138.com/iplookup.php?ip=${encodeURIComponent(t.value.ip)}`,target:"_blank"},"在 ip138 查看",8,De)])),[[C,I.value]])]),_:1})])):(_(),g("span",Ce,"-"))]),_:1}),e(c,{label:"Session"},{default:a(()=>[i(u(t.value.session_id||"-"),1)]),_:1}),e(c,{label:"Request ID"},{default:a(()=>[t.value.request_id?(_(),g("span",{key:0,class:"request-id",title:"点击复制(可用于关联控制台日志与检索)",onClick:l[1]||(l[1]=R=>h(t.value.request_id))},u(t.value.request_id),1)):(_(),g("span",$e,"-"))]),_:1}),e(c,{label:"输入 Token(含缓存读取)"},{default:a(()=>[i(u(m(T)(t.value.input_tokens??0)),1)]),_:1}),e(c,{label:"输出 Token"},{default:a(()=>[i(u(m(T)(t.value.output_tokens??0)),1)]),_:1}),e(c,{label:"缓存读取"},{default:a(()=>[i(u(m(T)(t.value.cache_read_tokens??0)),1)]),_:1}),e(c,{label:"请求耗时"},{default:a(()=>[i(u(m(le)(t.value.duration)),1)]),_:1}),e(c,{label:"缓存命中率"},{default:a(()=>[i(u(m(ae)(t.value)),1)]),_:1}),t.value.error?(_(),q(c,{key:0,label:"错误",span:2},{default:a(()=>[d("span",Ve,u(t.value.error),1)]),_:1})):j("",!0)]),_:1}),(n=t.value.response)!=null&&n.truncated?(_(),q(E,{key:0,class:"mt-16",type:"warning",closable:!1,title:"内容超过单请求捕获上限,已截断"})):j("",!0)]}),_:1}),(z=t.value.response)!=null&&z._capture?(_(),g(P,{key:0},[e(x,{label:"客户端请求",name:"inbound"},{default:a(()=>[d("div",Se,[e(D,{size:"small",class:"copy-btn",onClick:l[2]||(l[2]=n=>h(m(V)(t.value.response.inbound_request)))},{default:a(()=>[...l[14]||(l[14]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Ie,u(m(V)(t.value.response.inbound_request)),1)]),_:1}),e(x,{label:"转发上游的请求",name:"upstreamReq"},{default:a(()=>[d("div",qe,[e(D,{size:"small",class:"copy-btn",onClick:l[3]||(l[3]=n=>h(m(V)(t.value.response.upstream_request)))},{default:a(()=>[...l[15]||(l[15]=[i(" 复制 ",-1)])]),_:1})]),d("pre",ze,u(m(V)(t.value.response.upstream_request)),1)]),_:1}),t.value.response.upstream_response_text?(_(),q(x,{key:0,label:"上游响应(原始)",name:"upstreamRes"},{default:a(()=>[d("div",Re,[e(D,{size:"small",class:"copy-btn",onClick:l[4]||(l[4]=n=>h(t.value.response.upstream_response_text))},{default:a(()=>[...l[16]||(l[16]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Ue,u(m(fe)(t.value.response.upstream_response_text)),1)]),_:1})):(r=t.value.response.upstream_stream_frames)!=null&&r.length?(_(),q(x,{key:1,label:`上游响应(原始流,${t.value.response.upstream_stream_frames.length} 帧)`,name:"upstreamStream"},{default:a(()=>[d("div",je,[e(D,{size:"small",class:"copy-btn",onClick:l[5]||(l[5]=n=>h(t.value.response.upstream_stream_frames.join(`
1
+ import{d as G,a as b,j as H,o as _,c as q,w as a,e as g,g as e,h as i,t as u,k as m,b as Q,F as P,f as d,i as j,r as y,A,_ as X,p as pe,q as ce,a3 as _e,l as ve}from"./index-sPrBBnf5.js";import{u as me}from"./providers-di7L8jy_.js";import{u as Z}from"./useToast-D76qyWZg.js";import"./config-nvSp8hHp.js";async function ee(p){try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(p),!0}catch{}try{const f=document.createElement("textarea");f.value=p,f.style.position="fixed",f.style.opacity="0",document.body.appendChild(f),f.select();const k=document.execCommand("copy");return document.body.removeChild(f),k}catch{return!1}}const te=p=>{if(!p)return"-";const f=new Date(p),k=w=>String(w).padStart(2,"0");return`${f.getFullYear()}-${k(f.getMonth()+1)}-${k(f.getDate())} ${k(f.getHours())}:${k(f.getMinutes())}:${k(f.getSeconds())}`},V=p=>p?typeof p=="string"?p:JSON.stringify(p,null,2):"-",fe=p=>{if(!p)return"-";try{return JSON.stringify(JSON.parse(p),null,2)}catch{return p}},T=p=>p.toLocaleString(),le=p=>p==null||p<=0?"-":p>=1e3?(p/1e3).toFixed(2)+"s":Math.round(p)+"ms",ae=p=>{const f=p.input_tokens||0,k=p.cache_read_tokens||0;return f<=0?"-":(k/f*100).toFixed(1)+"%"},be={key:0,class:"detail-content"},ge={key:0,class:"ip-link"},ye={class:"ip-pop"},ke={class:"ip-pop-row"},he={key:0,class:"ip-pop-row"},xe={key:1,class:"ip-pop-error"},we={key:2},De=["href"],Ce={key:1},$e={key:1},Ve={class:"error-text"},Se={class:"pane-bar"},Ie={class:"json-block"},qe={class:"pane-bar"},ze={class:"json-block"},Re={class:"pane-bar"},Ue={class:"json-block"},je={class:"pane-bar"},Te={class:"json-block"},Ne={class:"pane-bar"},Le={class:"json-block"},Me={class:"pane-bar"},Ye={class:"json-block"},Ee={class:"pane-bar"},Pe={class:"json-block"},Fe={class:"pane-bar"},Be={class:"json-block"},Je=G({__name:"LogDetailDialog",setup(p,{expose:f}){const{toast:k}=Z(),w=y(!1),t=y(null),S=y("info"),I=y(!1);f({open:async v=>{w.value=!0,t.value=null,S.value="info";try{const l=v.id?`?id=${v.id}`:`?_file=${encodeURIComponent(v._file)}`,c=await A.get("/api/logs/detail"+l);c&&c.detail?t.value={...c.detail,...c.meta}:t.value=c}catch(l){console.error(l)}}});const h=async v=>{await ee(v)?k("已复制到剪贴板","success"):k("复制失败","error")},N=async v=>{if(!(v._ipInfo||v._ipInfoError)){I.value=!0;try{const l=await A.get(`/api/logs/ipinfo?ip=${encodeURIComponent(v.ip)}`);l.error?v._ipInfoError=l.error:v._ipInfo=l}catch{v._ipInfoError="查询失败"}finally{I.value=!1}}},M=v=>[v.country,v.region,v.city].filter(Boolean).join(" · ")||"-";return(v,l)=>{const c=b("el-descriptions-item"),Y=b("el-tag"),F=b("el-divider"),B=b("el-popover"),J=b("el-descriptions"),E=b("el-alert"),x=b("el-tab-pane"),D=b("el-button"),K=b("el-tabs"),L=b("el-dialog"),C=H("loading");return _(),q(L,{modelValue:w.value,"onUpdate:modelValue":l[11]||(l[11]=z=>w.value=z),title:"日志详情",width:"80%",top:"5vh","destroy-on-close":""},{default:a(()=>[t.value?(_(),g("div",be,[e(K,{modelValue:S.value,"onUpdate:modelValue":l[10]||(l[10]=z=>S.value=z)},{default:a(()=>{var z,r,s;return[e(x,{label:"基本信息",name:"info"},{default:a(()=>{var n;return[e(J,{column:2,border:""},{default:a(()=>[e(c,{label:"时间"},{default:a(()=>[i(u(m(te)(t.value.time)),1)]),_:1}),e(c,{label:"模型"},{default:a(()=>[i(u(t.value.model),1)]),_:1}),e(c,{label:"方法"},{default:a(()=>[i(u(t.value.method),1)]),_:1}),e(c,{label:"路径"},{default:a(()=>[i(u(t.value.path),1)]),_:1}),e(c,{label:"状态码"},{default:a(()=>[i(u(t.value.status),1)]),_:1}),e(c,{label:"流式"},{default:a(()=>[e(Y,{type:t.value.stream?"warning":"info"},{default:a(()=>[i(u(t.value.stream?"是":"否"),1)]),_:1},8,["type"])]),_:1}),e(c,{label:"IP"},{default:a(()=>[t.value.ip?(_(),g("span",ge,[e(B,{placement:"right",width:"240",trigger:"click",onShow:l[0]||(l[0]=R=>N(t.value))},{reference:a(()=>[d("span",null,u(t.value.ip),1)]),default:a(()=>[Q((_(),g("div",ye,[t.value._ipInfo?(_(),g(P,{key:0},[d("div",ke,[l[12]||(l[12]=d("span",{class:"ip-pop-label"},"归属地",-1)),d("span",null,u(M(t.value._ipInfo)),1)]),t.value._ipInfo.isp?(_(),g("div",he,[l[13]||(l[13]=d("span",{class:"ip-pop-label"},"运营商",-1)),d("span",null,u(t.value._ipInfo.isp),1)])):j("",!0)],64)):t.value._ipInfoError?(_(),g("div",xe,u(t.value._ipInfoError),1)):(_(),g("span",we,"查询中...")),e(F,{style:{margin:"8px 0"}}),d("a",{class:"ip-pop-link",href:`https://m.ip138.com/iplookup.php?ip=${encodeURIComponent(t.value.ip)}`,target:"_blank"},"在 ip138 查看",8,De)])),[[C,I.value]])]),_:1})])):(_(),g("span",Ce,"-"))]),_:1}),e(c,{label:"Session"},{default:a(()=>[i(u(t.value.session_id||"-"),1)]),_:1}),e(c,{label:"Request ID"},{default:a(()=>[t.value.request_id?(_(),g("span",{key:0,class:"request-id",title:"点击复制(可用于关联控制台日志与检索)",onClick:l[1]||(l[1]=R=>h(t.value.request_id))},u(t.value.request_id),1)):(_(),g("span",$e,"-"))]),_:1}),e(c,{label:"输入 Token(含缓存读取)"},{default:a(()=>[i(u(m(T)(t.value.input_tokens??0)),1)]),_:1}),e(c,{label:"输出 Token"},{default:a(()=>[i(u(m(T)(t.value.output_tokens??0)),1)]),_:1}),e(c,{label:"缓存读取"},{default:a(()=>[i(u(m(T)(t.value.cache_read_tokens??0)),1)]),_:1}),e(c,{label:"请求耗时"},{default:a(()=>[i(u(m(le)(t.value.duration)),1)]),_:1}),e(c,{label:"缓存命中率"},{default:a(()=>[i(u(m(ae)(t.value)),1)]),_:1}),t.value.error?(_(),q(c,{key:0,label:"错误",span:2},{default:a(()=>[d("span",Ve,u(t.value.error),1)]),_:1})):j("",!0)]),_:1}),(n=t.value.response)!=null&&n.truncated?(_(),q(E,{key:0,class:"mt-16",type:"warning",closable:!1,title:"内容超过单请求捕获上限,已截断"})):j("",!0)]}),_:1}),(z=t.value.response)!=null&&z._capture?(_(),g(P,{key:0},[e(x,{label:"客户端请求",name:"inbound"},{default:a(()=>[d("div",Se,[e(D,{size:"small",class:"copy-btn",onClick:l[2]||(l[2]=n=>h(m(V)(t.value.response.inbound_request)))},{default:a(()=>[...l[14]||(l[14]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Ie,u(m(V)(t.value.response.inbound_request)),1)]),_:1}),e(x,{label:"转发上游的请求",name:"upstreamReq"},{default:a(()=>[d("div",qe,[e(D,{size:"small",class:"copy-btn",onClick:l[3]||(l[3]=n=>h(m(V)(t.value.response.upstream_request)))},{default:a(()=>[...l[15]||(l[15]=[i(" 复制 ",-1)])]),_:1})]),d("pre",ze,u(m(V)(t.value.response.upstream_request)),1)]),_:1}),t.value.response.upstream_response_text?(_(),q(x,{key:0,label:"上游响应(原始)",name:"upstreamRes"},{default:a(()=>[d("div",Re,[e(D,{size:"small",class:"copy-btn",onClick:l[4]||(l[4]=n=>h(t.value.response.upstream_response_text))},{default:a(()=>[...l[16]||(l[16]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Ue,u(m(fe)(t.value.response.upstream_response_text)),1)]),_:1})):(r=t.value.response.upstream_stream_frames)!=null&&r.length?(_(),q(x,{key:1,label:`上游响应(原始流,${t.value.response.upstream_stream_frames.length} 帧)`,name:"upstreamStream"},{default:a(()=>[d("div",je,[e(D,{size:"small",class:"copy-btn",onClick:l[5]||(l[5]=n=>h(t.value.response.upstream_stream_frames.join(`
2
2
 
3
3
  `)))},{default:a(()=>[...l[17]||(l[17]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Te,u(t.value.response.upstream_stream_frames.join(`
4
4
 
@@ -0,0 +1 @@
1
+ @charset "UTF-8";.proc-name[data-v-43da5cf5]{display:flex;align-items:center;gap:8px;font-weight:600}.proc-name .proc-sub[data-v-43da5cf5]{font-weight:400;color:var(--el-text-color-secondary);font-size:12px}.status-dot[data-v-43da5cf5]{display:inline-block;width:8px;height:8px;border-radius:50%}.status-dot.running[data-v-43da5cf5]{background:var(--el-color-success);box-shadow:0 0 4px var(--el-color-success)}.status-dot.stopped[data-v-43da5cf5]{background:var(--el-color-info)}.status-dot.abnormal[data-v-43da5cf5]{background:var(--el-color-danger)}.desc-cell[data-v-43da5cf5]{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.desc-full[data-v-43da5cf5]{margin:0;line-height:1.8}.mr-8[data-v-43da5cf5]{margin-right:8px}
@@ -0,0 +1 @@
1
+ import{d as X,p as q,a8 as H,b as J,e as D,g as s,w as l,r as i,a as c,j as Q,o as w,f as d,$ as W,h as o,t as r,c as Y,E as Z,_ as ee}from"./index-sPrBBnf5.js";import{m as E}from"./useManagerApi-qPjxfB0X.js";import{u as te}from"./useToast-D76qyWZg.js";const ae={class:"flex-col-gap"},le={class:"flex-between"},se={class:"proc-name"},ne={class:"proc-sub"},oe=["onClick"],re={key:1,class:"muted-sm"},ie={class:"desc-full"},de=X({__name:"ProcessView",setup(ue){const{toast:b}=te(),v=i(!0),g=i(!1),y=i([]),x=i(null),p=i(null),h=i(Date.now()),I={main:"代理服务"},C=e=>I[e.role]??e.role,k={main:"代理服务(ellm-proxy):网关本体,转发 /v1/* 模型请求,同时承载管理台页面与全部管理 API;本页的重启操作即作用于该进程——服务会先孵化新进程接管端口再退出,期间网关与管理台短暂不可用。"},O=e=>k[e.role],_=i(!1),T=i(""),$=i(""),P=e=>{T.value=`${C(e)}(${M()})`,$.value=k[e.role],_.value=!0},M=e=>"ellm-proxy",A=e=>e.status==="online",N=e=>e.status?e.status==="online"?"running":e.status==="errored"?"abnormal":"stopped":"stopped",V=e=>{switch(e.status){case"online":return{text:"运行中",type:"success"};case"stopped":return{text:"已停止",type:"info"};case"errored":return{text:"异常",type:"danger"};case"waiting restart":case"launching":return{text:"重启中",type:"warning"};default:return{text:e.status||"未知",type:"info"}}},R=e=>{if(!A(e)||!e.startedAt)return"-";let t=Math.max(0,Math.floor((h.value-e.startedAt)/1e3));const f=Math.floor(t/86400);t-=f*86400;const u=Math.floor(t/3600);t-=u*3600;const n=Math.floor(t/60);return t-=n*60,[f?`${f}天`:"",u?`${u}时`:"",n?`${n}分`:"",`${t}秒`].filter(Boolean).join(" ")},S=e=>e?e>=1024**3?(e/1024**3).toFixed(1)+"G":e>=1024**2?(e/1024**2).toFixed(1)+"M":Math.round(e/1024)+"K":"-",m=async e=>{e||(y.value.length?g.value=!0:v.value=!0);try{y.value=await E.processes(),h.value=Date.now(),x.value=null}catch(t){e||(x.value=t.message)}finally{e||(v.value=!1,g.value=!1)}};let B;const j=()=>{p.value||document.hidden||m(!0)};q(()=>{m(!1),B=window.setInterval(j,5e3)}),H(()=>window.clearInterval(B));const z=async()=>{try{await Z.confirm("确定重启 ellm-proxy 代理服务?重启期间网关与管理台将短暂不可用(服务自动孵化新进程接管端口)。","重启确认",{confirmButtonText:"重启",cancelButtonText:"取消",type:"warning"})}catch{return}p.value="restart";try{await E.restart(),b("重启指令已发送,服务重启中","success")}catch(e){b(e.message||"操作失败","error")}finally{p.value=null,await m(!0)}};return(e,t)=>{const f=c("el-tag"),u=c("el-button"),n=c("el-table-column"),F=c("el-empty"),L=c("el-table"),U=c("el-card"),G=c("el-dialog"),K=Q("loading");return J((w(),D("div",ae,[s(U,{shadow:"never"},{header:l(()=>[d("div",le,[t[6]||(t[6]=d("span",{class:"fw-600"},"进程管理",-1)),d("div",null,[s(f,{type:"info",class:"mr-8"},{default:l(()=>[...t[4]||(t[4]=[o("刷新间隔 5s",-1)])]),_:1}),s(u,{loading:g.value,onClick:t[0]||(t[0]=a=>m(!1))},{default:l(()=>[...t[5]||(t[5]=[o("刷新",-1)])]),_:1},8,["loading"])])])]),default:l(()=>[s(L,{data:y.value,border:"",stripe:"",class:"full-width"},{empty:l(()=>[s(F,{description:x.value||"暂无进程数据"},{default:l(()=>[s(u,{type:"primary",onClick:t[1]||(t[1]=a=>m(!1))},{default:l(()=>[...t[8]||(t[8]=[o("重试",-1)])]),_:1})]),_:1},8,["description"])]),default:l(()=>[s(n,{label:"进程","min-width":"180"},{default:l(({row:a})=>[d("div",se,[d("span",{class:W(["status-dot",N(a)])},null,2),o(" "+r(C(a))+" ",1),d("span",ne,r(M(a)),1)])]),_:1}),s(n,{label:"运行状态",width:"100",align:"center"},{default:l(({row:a})=>[s(f,{type:V(a).type,"disable-transitions":""},{default:l(()=>[o(r(V(a).text),1)]),_:2},1032,["type"])]),_:1}),s(n,{label:"PID",width:"90",align:"center"},{default:l(({row:a})=>[o(r(a.pid??"-"),1)]),_:1}),s(n,{label:"端口",width:"90",align:"center"},{default:l(({row:a})=>[o(r(a.port??"-"),1)]),_:1}),s(n,{label:"运行时长",width:"130",align:"center"},{default:l(({row:a})=>[o(r(R(a)),1)]),_:1}),s(n,{label:"地址","min-width":"170"},{default:l(({row:a})=>[o(r(a.address??"-"),1)]),_:1}),s(n,{label:"内存",width:"90",align:"center"},{default:l(({row:a})=>[o(r(S(a.memory)),1)]),_:1}),s(n,{label:"描述","min-width":"220"},{default:l(({row:a})=>[d("span",{class:"desc-cell",title:"点击查看详情",onClick:ce=>P(a)},r(O(a)),9,oe)]),_:1}),s(n,{label:"操作",width:"90",align:"center"},{default:l(({row:a})=>[a.role==="main"?(w(),Y(u,{key:0,type:"primary",size:"small",loading:p.value==="restart",disabled:p.value!==null,onClick:z},{default:l(()=>[...t[7]||(t[7]=[o(" 重启 ",-1)])]),_:1},8,["loading","disabled"])):(w(),D("span",re,"不可操作"))]),_:1})]),_:1},8,["data"]),t[9]||(t[9]=d("div",{class:"muted-sm"}," 服务为单进程架构(网关、管理台 API 与界面同进程同端口):重启期间网关与管理台短暂不可用, 服务会自动孵化新进程接管端口,无需手动拉起。 ",-1))]),_:1}),s(G,{modelValue:_.value,"onUpdate:modelValue":t[3]||(t[3]=a=>_.value=a),title:T.value,width:"520px"},{footer:l(()=>[s(u,{type:"primary",onClick:t[2]||(t[2]=a=>_.value=!1)},{default:l(()=>[...t[10]||(t[10]=[o("知道了",-1)])]),_:1})]),default:l(()=>[d("p",ie,r($.value),1)]),_:1},8,["modelValue","title"])])),[[K,v.value]])}}}),_e=ee(de,[["__scopeId","data-v-43da5cf5"]]);export{_e as default};
@@ -1,4 +1,4 @@
1
- import{N as Kn,s as Mt,v as He,x as cr,y as Lt,z as jo,B as Ts,C as ia,D as Os,G as aa,H as Is,I as Kt,J as sa,K as xi,L as Ns,M as Cs,O as As,P as xs,Q as Ps,R as Xr,S as Ds,T as Kr,U as Rs,V as Ms,W as Ls,X as ws,Y as gn,A as Yt,d as Jt,a as J,o as te,c as Me,w as M,f as K,t as Te,e as Be,F as Bo,q as ko,Z as fo,$ as Vs,k as q,h as ae,i as tt,g as P,a0 as Fs,l as Ht,r as Pe,_ as cn,a1 as Xo,a2 as Rt,p as la,a3 as $s,E as Ao,a4 as Us,a5 as js,a6 as Bs,a7 as ks}from"./index-X5PdkJlA.js";import{u as un}from"./useToast-MtbkGJiM.js";import{u as Ko}from"./providers-H_9x778D.js";import"./config-CRWaUFQc.js";var yr={exports:{}},po={exports:{}},ho={};/**
1
+ import{N as Kn,s as Mt,v as He,x as cr,y as Lt,z as jo,B as Ts,C as ia,D as Os,G as aa,H as Is,I as Kt,J as sa,K as xi,L as Ns,M as Cs,O as As,P as xs,Q as Ps,R as Xr,S as Ds,T as Kr,U as Rs,V as Ms,W as Ls,X as ws,Y as gn,A as Yt,d as Jt,a as J,o as te,c as Me,w as M,f as K,t as Te,e as Be,F as Bo,q as ko,Z as fo,$ as Vs,k as q,h as ae,i as tt,g as P,a0 as Fs,l as Ht,r as Pe,_ as cn,a1 as Xo,a2 as Rt,p as la,a3 as $s,E as Ao,a4 as Us,a5 as js,a6 as Bs,a7 as ks}from"./index-sPrBBnf5.js";import{u as un}from"./useToast-D76qyWZg.js";import{u as Ko}from"./providers-di7L8jy_.js";import"./config-nvSp8hHp.js";var yr={exports:{}},po={exports:{}},ho={};/**
2
2
  * @vue/compiler-core v3.5.42
3
3
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
4
4
  * @license MIT
@@ -0,0 +1 @@
1
+ import{u as F}from"./useToast-D76qyWZg.js";import{d as Y,p as X,b as q,o as m,c as B,w as n,e as x,f as s,t as $,i as K,h as i,g as a,F as H,r,E as W,a as c,j as G,_ as Q,l as j,A as ee}from"./index-sPrBBnf5.js";import{u as le}from"./config-nvSp8hHp.js";import{m as I,w as te}from"./useManagerApi-qPjxfB0X.js";const se={class:"flex-between"},ae={class:"muted-sm mb-8"},oe={class:"switch-row"},ne={class:"switch-info"},ie={class:"switch-title"},ue={class:"switch-row"},de={class:"switch-info"},re={class:"switch-title"},ce={class:"switch-row"},ve={class:"switch-info"},pe={class:"switch-title"},me=Y({__name:"HarnessSwitches",setup(J){const{toast:w}=F(),k=r(!0),U=r(!1),v=r(null),A=r(null),d=r(null),V=r(null),y=r("success"),h=async _=>{_||(v.value?U.value=!0:k.value=!0);try{v.value=await I.harness(),A.value=null}catch(o){_||(A.value=o.message)}finally{_||(k.value=!1,U.value=!1)}};X(()=>h(!1));const E={claudeCode:{label:"Claude Code",file:"~/.claude/settings.json",toggle:I.setClaudeCode},codex:{label:"Codex CLI",file:"~/.codex/config.toml",toggle:I.setCodex},dsh:{label:"DeepSeek Harness",file:"~/.dsh/settings.yaml",toggle:I.setDsh}},g=async(_,o)=>{if(!v.value||d.value)return;const b=E[_],p=o?`确定开启 ${b.label} 代理?将改写 ${b.file}(开启前自动备份,关闭时从备份还原)。`:`确定关闭 ${b.label} 代理?将从备份还原 ${b.file}(无备份时移除代理相关配置)。`;try{await W.confirm(p,o?"开启确认":"关闭确认",{confirmButtonText:o?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}d.value=_;try{const l=await b.toggle(o);V.value=l.note??null,y.value=o?"success":"info",w(o?"已开启代理":"已关闭代理","success")}catch(l){w(l.message||"操作失败","error")}finally{d.value=null,await h(!0)}};return(_,o)=>{const b=c("el-button"),p=c("el-alert"),l=c("el-tag"),P=c("el-switch"),z=c("el-divider"),D=c("el-empty"),R=c("el-card"),N=G("loading");return q((m(),B(R,{shadow:"never"},{header:n(()=>[s("div",se,[o[7]||(o[7]=s("span",{class:"fw-600"},"代理开关",-1)),a(b,{loading:U.value,onClick:o[0]||(o[0]=T=>h(!1))},{default:n(()=>[...o[6]||(o[6]=[i("刷新",-1)])]),_:1},8,["loading"])])]),default:n(()=>[v.value?(m(),x(H,{key:0},[s("div",ae," 将本机 CLI 工具的流量一键指向 ellm-proxy 网关("+$(v.value.gatewayBase)+")。 开启前自动备份原配置为 *.ellm-bak(保留最早的原始备份),关闭时从备份还原。 ",1),V.value?(m(),B(p,{key:0,title:V.value,type:y.value,"show-icon":"",class:"mb-8",onClose:o[1]||(o[1]=T=>V.value=null)},null,8,["title","type"])):K("",!0),s("div",oe,[s("div",ne,[s("div",ie,[o[8]||(o[8]=i(" Claude Code ",-1)),a(l,{type:v.value.claudeCode?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i($(v.value.claudeCode?"已开启":"未开启"),1)]),_:1},8,["type"])]),o[9]||(o[9]=s("div",{class:"switch-desc"},[i(" 写入 "),s("code",null,"~/.claude/settings.json"),i(" 的 env.ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN, 鉴权取网关配置的 masterKey(未设置将开启失败);其余字段原样保留,重启 Claude Code 会话后生效。 ")],-1))]),a(P,{"model-value":v.value.claudeCode,loading:d.value==="claudeCode",onChange:o[2]||(o[2]=T=>g("claudeCode",!!T))},null,8,["model-value","loading"])]),a(z,{class:"switch-divider"}),s("div",ue,[s("div",de,[s("div",re,[o[10]||(o[10]=i(" Codex CLI ",-1)),a(l,{type:v.value.codex?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i($(v.value.codex?"已开启":"未开启"),1)]),_:1},8,["type"])]),o[11]||(o[11]=s("div",{class:"switch-desc"},[i(" 写入 "),s("code",null,"~/.codex/config.toml"),i(" 的 model_provider 与 [model_providers.ellm-proxy] 段 (保留用户注释与排版);开启后需按提示设置环境变量 ELLM_PROXY_API_KEY 供 Codex 鉴权。 ")],-1))]),a(P,{"model-value":v.value.codex,loading:d.value==="codex",onChange:o[3]||(o[3]=T=>g("codex",!!T))},null,8,["model-value","loading"])]),a(z,{class:"switch-divider"}),s("div",ce,[s("div",ve,[s("div",pe,[o[12]||(o[12]=i(" DeepSeek Harness ",-1)),a(l,{type:v.value.dsh?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i($(v.value.dsh?"已开启":"未开启"),1)]),_:1},8,["type"])]),o[13]||(o[13]=s("div",{class:"switch-desc"},[i(" 写入 "),s("code",null,"~/.dsh/settings.yaml"),i(" 的 llm-deepseek.baseURL 与 "),s("code",null,"~/.dsh/.credentials.yaml"),i(" 的 DEEPSEEK_API_KEY(取网关 masterKey,未设置则写占位值);模型请在 dsh 内选择与网关部署名一致的模型。 dsh 为 developer preview,配置格式可能随版本变化。 ")],-1))]),a(P,{"model-value":v.value.dsh,loading:d.value==="dsh",onChange:o[4]||(o[4]=T=>g("dsh",!!T))},null,8,["model-value","loading"])])],64)):(m(),B(D,{key:1,description:A.value||"暂无开关状态"},{default:n(()=>[a(b,{type:"primary",onClick:o[5]||(o[5]=T=>h(!1))},{default:n(()=>[...o[14]||(o[14]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1})),[[N,k.value]])}}}),fe=Q(me,[["__scopeId","data-v-65e65156"]]),_e={class:"service-settings"},ge={class:"flex-between"},ye={key:0,class:"switch-row"},we={class:"switch-info"},be={class:"switch-title"},Ce={key:0,class:"switch-row"},ke={class:"switch-info"},he={class:"switch-title"},$e={class:"switch-desc"},Ve={key:0,class:"version-block"},xe={class:"version-row"},Ae={class:"muted-sm"},Te={key:0,class:"muted-sm"},Ue={class:"version-actions"},Ee=Y({__name:"ServiceUpdateSettings",setup(J){const{toast:w}=F(),k=r(!0),U=r(!1),v=r(null),A=r(null),d=r(null),V=r(null),y=r(null),h=r(!1),E=r(!1),g=r(null),_=async t=>{var e,u;if(!t){if(U.value)return;U.value=!0}try{const[f,S]=await Promise.allSettled([I.autostart(),I.update()]);v.value=f.status==="fulfilled"?f.value:null,A.value=f.status==="rejected"?(e=f.reason)==null?void 0:e.message:null,d.value=S.status==="fulfilled"?S.value:null,V.value=S.status==="rejected"?(u=S.reason)==null?void 0:u.message:null}finally{k.value=!1,U.value=!1}};X(()=>_(!1));const o=j(()=>{var t,e;return((e=(t=d.value)==null?void 0:t.lastCheck)==null?void 0:e.currentVersion)??null}),b=j(()=>{var t,e;return((e=(t=d.value)==null?void 0:t.lastCheck)==null?void 0:e.latestVersion)??null}),p=j(()=>{var t,e;return((e=(t=d.value)==null?void 0:t.lastCheck)==null?void 0:e.hasUpdate)===!0}),l=j(()=>{var e,u;const t=(u=(e=d.value)==null?void 0:e.lastCheck)==null?void 0:u.checkedAt;return t?new Date(t).toLocaleString():null}),P=j(()=>{var u;const t=(u=d.value)==null?void 0:u.lastApply;if(!t)return null;const e=t.appliedAt?new Date(t.appliedAt).toLocaleString():"";return`${t.fromVersion} → ${t.toVersion}${e?`(${e})`:""}`}),z=t=>t?new Date(t).toLocaleString():"",D=async t=>{if(!v.value||y.value)return;const e=t?"确定开启开机自启?将注册登录触发的自启项(登录时自动恢复服务)。":"确定关闭开机自启?将移除自启项,且此后首次启动不再自动注册。";try{await W.confirm(e,t?"开启确认":"关闭确认",{confirmButtonText:t?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}y.value="autostart";try{const u=await I.setAutostart(t);u.note?w(u.note,"success"):w(t?"开机自启已开启":"开机自启已关闭","success")}catch(u){w(u.message||"操作失败","error")}finally{y.value=null,await _(!0)}},R=async t=>{if(!d.value||y.value)return;const e=t?"确定开启自动更新?将每小时检查私有源,发现新版本自动安装并重启服务。":"确定关闭自动更新?不再自动检查(手动检查更新不受影响)。";try{await W.confirm(e,t?"开启确认":"关闭确认",{confirmButtonText:t?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}y.value="updateEnabled";try{const u=await I.setUpdateEnabled(t);u.note&&w(u.note,"success")}catch(u){w(u.message||"操作失败","error")}finally{y.value=null,await _(!0)}},N=async()=>{h.value=!0;try{const t=await I.checkUpdate();g.value=t.hasUpdate?`发现新版本 ${t.latestVersion}(当前 ${t.currentVersion}),可点击「立即更新」安装`:`已是最新版本 ${t.currentVersion}(检查于 ${z(t.checkedAt)})`,await _(!0)}catch(t){w(t.message||"检查更新失败","error")}finally{h.value=!1}},T=async()=>{var t;try{await W.confirm(`确定更新到 ${b.value}?服务将自动重启并在启动时安装新版本(期间界面请求会短暂失败,属正常现象)。`,"更新确认",{confirmButtonText:"立即更新",cancelButtonText:"取消",type:"warning"})}catch{return}E.value=!0;try{const e=await I.update(),u=await I.applyUpdate();if(!u.updated){g.value=`已是最新版本 ${u.toVersion}`;return}g.value=`正在重启服务并安装 ${u.toVersion},请稍候(预计 1~3 分钟)...`;const f=await te({targetVersion:u.toVersion,sinceAppliedAt:((t=e.lastApply)==null?void 0:t.appliedAt)??null});f&&!f.error?(w(`已更新到 ${f.toVersion}`,"success"),g.value=`已更新 ${f.fromVersion} → ${f.toVersion}`):f!=null&&f.error?(w(`更新失败:${f.error}`,"error"),g.value=`更新失败:${f.error}(服务已恢复旧版本)`):g.value="更新耗时较长,请稍后刷新页面查看结果"}catch(e){w(e.message||"更新失败","error")}finally{E.value=!1,await _(!0)}};return(t,e)=>{const u=c("el-button"),f=c("el-tag"),S=c("el-switch"),M=c("el-empty"),O=c("el-card"),C=c("el-alert"),Z=G("loading");return q((m(),x("div",_e,[a(O,{shadow:"never",class:"mb-16"},{header:n(()=>[s("div",ge,[e[7]||(e[7]=s("span",{class:"fw-600"},"开机自启",-1)),a(u,{loading:U.value,onClick:e[0]||(e[0]=L=>_(!1))},{default:n(()=>[...e[6]||(e[6]=[i("刷新",-1)])]),_:1},8,["loading"])])]),default:n(()=>[v.value?(m(),x("div",ye,[s("div",we,[s("div",be,[e[8]||(e[8]=i(" 登录时自动启动服务 ",-1)),a(f,{type:v.value.enabled?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i($(v.value.enabled?"已开启":"未开启"),1)]),_:1},8,["type"])]),e[9]||(e[9]=s("div",{class:"switch-desc"},[i(" 登录操作系统时自动启动服务(无需管理员权限)。 关闭后首次启动 "),s("code",null,"ellm-proxy"),i(" 也不会再自动注册,重新开启即可解除。 ")],-1))]),a(S,{"model-value":v.value.enabled,loading:y.value==="autostart",onChange:e[1]||(e[1]=L=>D(!!L))},null,8,["model-value","loading"])])):(m(),B(M,{key:1,description:A.value||"暂无自启状态"},{default:n(()=>[a(u,{type:"primary",onClick:e[2]||(e[2]=L=>_(!1))},{default:n(()=>[...e[10]||(e[10]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1}),a(O,{shadow:"never",class:"mb-16"},{header:n(()=>[...e[11]||(e[11]=[s("div",{class:"flex-between"},[s("span",{class:"fw-600"},"自动更新")],-1)])]),default:n(()=>[d.value?(m(),x("div",Ce,[s("div",ke,[s("div",he,[e[12]||(e[12]=i(" 自动检查并安装新版本 ",-1)),a(f,{type:d.value.enabled?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i($(d.value.enabled?"已开启":"未开启"),1)]),_:1},8,["type"])]),s("div",$e,[d.value.supported?(m(),x(H,{key:1},[i(" 每小时检查 npm 源,发现新版本自动重启服务并在启动时安装(更新期间服务短暂中断); 关闭后仍可在下方手动检查与更新。 ")],64)):(m(),x(H,{key:0},[i(" 当前为源码运行模式,更新功能不可用(仅 npm 全局安装的包支持自动更新)。 ")],64))])]),d.value.supported?(m(),B(S,{key:0,"model-value":d.value.enabled,loading:y.value==="updateEnabled",onChange:e[3]||(e[3]=L=>R(!!L))},null,8,["model-value","loading"])):K("",!0)])):(m(),B(M,{key:1,description:V.value||"暂无更新状态"},{default:n(()=>[a(u,{type:"primary",onClick:e[4]||(e[4]=L=>_(!1))},{default:n(()=>[...e[13]||(e[13]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1}),a(O,{shadow:"never"},{header:n(()=>[...e[14]||(e[14]=[s("div",{class:"flex-between"},[s("span",{class:"fw-600"},"版本信息")],-1)])]),default:n(()=>[d.value?(m(),x("div",Ve,[s("div",xe,[e[17]||(e[17]=s("span",{class:"version-label"},"当前版本",-1)),a(f,{size:"small"},{default:n(()=>[i($(o.value||"未知"),1)]),_:1}),b.value?(m(),x(H,{key:0},[e[16]||(e[16]=s("span",{class:"version-label"},"最新版本",-1)),a(f,{type:p.value?"warning":"success",size:"small","disable-transitions":""},{default:n(()=>[i($(b.value),1)]),_:1},8,["type"]),p.value?(m(),B(f,{key:0,type:"warning",size:"small","disable-transitions":""},{default:n(()=>[...e[15]||(e[15]=[i("发现新版本",-1)])]),_:1})):K("",!0)],64)):K("",!0)]),s("div",Ae,[i(" 上次检查:"+$(l.value||"尚未检查")+" ",1),d.value.updating?(m(),x(H,{key:0},[i(" · 更新执行中...")],64)):K("",!0)]),P.value?(m(),x("div",Te,"上次更新:"+$(P.value),1)):K("",!0),s("div",Ue,[a(u,{loading:h.value,disabled:d.value.updating,onClick:N},{default:n(()=>[i($(h.value?"检查中...":"检查更新"),1)]),_:1},8,["loading","disabled"]),p.value?(m(),B(u,{key:0,type:"primary",loading:E.value,disabled:d.value.updating,onClick:T},{default:n(()=>[i($(E.value?"更新中...":`立即更新到 ${b.value}`),1)]),_:1},8,["loading","disabled"])):K("",!0)]),g.value?(m(),B(C,{key:1,title:g.value,type:"info","show-icon":"",class:"mt-8",onClose:e[5]||(e[5]=L=>g.value=null)},null,8,["title"])):K("",!0)])):(m(),B(M,{key:1,description:"更新状态不可用"}))]),_:1})])),[[Z,k.value]])}}}),Se=Q(Ee,[["__scopeId","data-v-9366cd99"]]),Be={class:"flex-col-gap"},Ie={class:"flex-between"},Pe={class:"muted-sm"},De={key:0},Ke={class:"flex-end"},Re=Y({__name:"SettingsView",setup(J){const{toast:w}=F(),k=le(),U=r("basic"),v=r(!0),A=r(!1),d=r(""),V=r(29770),y=r(null),h=r("passthrough"),E=r(!1),g=r(""),_=r(""),o=async()=>{var p;try{await k.load(),d.value=k.config.masterKey||"",V.value=k.config.port??29770,h.value=k.config.compression==="plain"?"plain":"passthrough",E.value=k.config.logContent===!0;const l=(p=k.config["web-ui"])==null?void 0:p["login-account"];g.value=(l==null?void 0:l.username)||"",_.value=(l==null?void 0:l.password)||"";try{y.value=await ee.get("/api/status").then(P=>P.listenPort)}catch{y.value=null}}catch(l){w(l.message,"error")}finally{v.value=!1}},b=async()=>{A.value=!0;try{await k.update(p=>{var l;p.masterKey=d.value.trim(),p.port=V.value,p.compression=h.value,p.logContent=E.value,p["web-ui"]??(p["web-ui"]={}),(l=p["web-ui"])["login-account"]??(l["login-account"]={}),p["web-ui"]["login-account"].username=g.value.trim(),p["web-ui"]["login-account"].password=_.value}),w("设置已保存并生效","success")}catch(p){w(p.message,"error")}finally{A.value=!1}};return X(o),(p,l)=>{const P=c("el-tag"),z=c("el-input"),D=c("el-form-item"),R=c("el-input-number"),N=c("el-option"),T=c("el-select"),t=c("el-switch"),e=c("el-form"),u=c("el-card"),f=c("el-button"),S=c("el-tab-pane"),M=c("el-tabs"),O=G("loading");return m(),B(M,{modelValue:U.value,"onUpdate:modelValue":l[6]||(l[6]=C=>U.value=C),class:"settings-tabs"},{default:n(()=>[a(S,{label:"基本设置",name:"basic"},{default:n(()=>[q((m(),x("div",Be,[a(u,{shadow:"never"},{header:n(()=>[s("div",Ie,[l[8]||(l[8]=s("span",{class:"fw-600"},"基本设置",-1)),a(P,{type:"success"},{default:n(()=>[...l[7]||(l[7]=[i("运行中",-1)])]),_:1})])]),default:n(()=>[a(e,{"label-position":"top"},{default:n(()=>[a(D,{label:"Master Key"},{default:n(()=>[a(z,{modelValue:d.value,"onUpdate:modelValue":l[0]||(l[0]=C=>d.value=C),type:"password",placeholder:"sk-1234","show-password":""},null,8,["modelValue"]),l[9]||(l[9]=s("div",{class:"muted-sm"}," 网关 API 认证密钥(/v1/* 请求需携带 Bearer <masterKey>;留空 = 本地放行) ",-1))]),_:1}),a(D,{label:"监听端口 (port)"},{default:n(()=>[a(R,{modelValue:V.value,"onUpdate:modelValue":l[1]||(l[1]=C=>V.value=C),min:1,max:65535,step:1,class:"full-width"},null,8,["modelValue"]),s("div",Pe,[l[11]||(l[11]=i(" 网关与管理台 API 的监听端口,保存后下次重启生效 ",-1)),y.value!==null?(m(),x(H,{key:0},[l[10]||(l[10]=i(" ;当前实际监听 ",-1)),s("b",null,$(y.value),1),y.value!==V.value?(m(),x("span",De,"(由 PORT 环境变量覆盖,此配置值不生效)")):K("",!0)],64)):K("",!0)])]),_:1}),a(D,{label:"压缩语义 (compression)"},{default:n(()=>[a(T,{modelValue:h.value,"onUpdate:modelValue":l[2]||(l[2]=C=>h.value=C),class:"full-width"},{default:n(()=>[a(N,{label:"透传 (passthrough)",value:"passthrough"}),a(N,{label:"明文 (plain)",value:"plain"})]),_:1},8,["modelValue"]),l[12]||(l[12]=s("div",{class:"muted-sm"},"上游响应压缩处理方式,保存后热更新生效",-1))]),_:1}),a(D,{label:"日志内容捕获 (logContent)"},{default:n(()=>[a(t,{modelValue:E.value,"onUpdate:modelValue":l[3]||(l[3]=C=>E.value=C)},null,8,["modelValue"]),l[13]||(l[13]=s("div",{class:"muted-sm"}," 记录完整请求/响应(含上游原始与转换后内容)进日志详情,用于排查定位问题;开启后有少量内存与存储开销,保存后热更新生效 ",-1))]),_:1}),a(D,{label:"管理台用户名 (web-ui.login-account.username)"},{default:n(()=>[a(z,{modelValue:g.value,"onUpdate:modelValue":l[4]||(l[4]=C=>g.value=C),placeholder:"admin"},null,8,["modelValue"]),l[14]||(l[14]=s("div",{class:"muted-sm"}," 管理台登录用户名,留空回退 WEBUI_USERNAME 环境变量或默认 admin ",-1))]),_:1}),a(D,{label:"管理台密码 (web-ui.login-account.password)"},{default:n(()=>[a(z,{modelValue:_.value,"onUpdate:modelValue":l[5]||(l[5]=C=>_.value=C),type:"password",placeholder:"admin","show-password":""},null,8,["modelValue"]),l[15]||(l[15]=s("div",{class:"muted-sm"}," 管理台登录密码,留空回退 WEBUI_PASSWORD 环境变量或默认 admin;修改保存后立即生效,需用新账号重新登录 ",-1))]),_:1})]),_:1})]),_:1}),s("div",Ke,[a(f,{type:"primary",loading:A.value,disabled:A.value,onClick:b},{default:n(()=>[i($(A.value?"保存中...":"保存设置"),1)]),_:1},8,["loading","disabled"])])])),[[O,v.value]])]),_:1}),a(S,{label:"代理开关",name:"harness",lazy:""},{default:n(()=>[a(fe)]),_:1}),a(S,{label:"服务与更新",name:"service",lazy:""},{default:n(()=>[a(Se)]),_:1})]),_:1},8,["modelValue"])}}});export{Re as default};
@@ -0,0 +1 @@
1
+ .switch-row[data-v-65e65156]{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:4px 0}.switch-title[data-v-65e65156]{display:flex;align-items:center;gap:8px;font-weight:600;margin-bottom:4px}.switch-desc[data-v-65e65156]{color:var(--el-text-color-secondary);font-size:13px;line-height:1.6}.switch-desc code[data-v-65e65156]{background:var(--el-fill-color-light);padding:1px 6px;border-radius:4px;font-size:12px}.switch-divider[data-v-65e65156]{margin:16px 0}.mb-16[data-v-9366cd99]{margin-bottom:16px}.mt-8[data-v-9366cd99]{margin-top:8px}.switch-row[data-v-9366cd99]{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:4px 0}.switch-title[data-v-9366cd99]{display:flex;align-items:center;gap:8px;font-weight:600;margin-bottom:4px}.switch-desc[data-v-9366cd99]{color:var(--el-text-color-secondary);font-size:13px;line-height:1.6}.switch-desc code[data-v-9366cd99]{background:var(--el-fill-color-light);padding:1px 6px;border-radius:4px;font-size:12px}.version-row[data-v-9366cd99]{display:flex;align-items:center;gap:8px;margin-bottom:8px}.version-label[data-v-9366cd99]{color:var(--el-text-color-secondary);font-size:13px}.version-actions[data-v-9366cd99]{margin-top:12px;display:flex;gap:8px}
@@ -1 +1 @@
1
- import{d as J,p as O,aa as Q,e as S,g as a,w as l,r as i,a as r,o as s,f as k,F as x,h as o,c as p,i as K,t as I,Z as W,l as z,A,a9 as v,ab as X,m as Y,_ as h}from"./index-X5PdkJlA.js";const ee={class:"setup-wrap"},ae={class:"wizard-actions"},le={class:"key-row"},te={class:"wizard-actions"},se={class:"wizard-actions"},oe=J({__name:"SetupView",setup(ne){const P=Y(),m=i(0),n=i(""),d=i("generate"),b=i(""),M=i(""),_=i(""),c=i(""),U=i(""),B=i(!1),E=i(!1),w=i(!1);O(async()=>{const u=await Q();w.value=u.masterKeySet,w.value&&(d.value="skip")});const T=z(()=>n.value==="local"||w.value),R=z(()=>w.value?"(保留现有主密钥)":"(/v1 保持免鉴权)"),D=z(()=>d.value==="skip"?T.value:d.value==="custom"?M.value.trim().length>=8:b.value!=="");async function H(){try{const u=await A.post("/api/setup/master-key",{mode:"generate"});b.value=u.masterKey}catch(u){v.error(u.message||"生成失败")}}async function Z(){try{await navigator.clipboard.writeText(b.value),v.success("已复制到剪贴板")}catch{v.error("复制失败,请手动选择复制")}}async function j(){if(d.value==="skip"){m.value=2;return}B.value=!0;try{d.value==="custom"&&await A.post("/api/setup/master-key",{mode:"custom",masterKey:M.value.trim()}),w.value=!0,m.value=2}catch(u){v.error(u.message||"保存失败")}finally{B.value=!1}}async function $(){const u=_.value!==""||c.value!=="";if(u||n.value==="server"){if(!_.value.trim()){v.warning("请输入用户名");return}if(c.value.length<6){v.warning("密码至少 6 位");return}if(c.value!==U.value){v.warning("两次输入的密码不一致");return}}E.value=!0;try{await A.post("/api/setup/finish",{deployment:n.value,...u?{username:_.value.trim(),password:c.value}:{}}),X(n.value),v.success("初始化完成,请使用新账号登录"),await P.push("/login")}catch(e){v.error(e.message||"保存失败")}finally{E.value=!1}}return(u,e)=>{const L=r("el-step"),q=r("el-steps"),g=r("el-radio"),F=r("el-radio-group"),f=r("el-form-item"),N=r("el-form"),V=r("el-alert"),y=r("el-button"),C=r("el-input"),G=r("el-card");return s(),S("div",ee,[a(G,{shadow:"hover",class:"setup-card"},{default:l(()=>[e[20]||(e[20]=k("h2",{class:"setup-title"},"ELLM 初始化向导",-1)),a(q,{active:m.value,"align-center":"","finish-status":"success",class:"mb-24"},{default:l(()=>[a(L,{title:"部署环境"}),a(L,{title:"主密钥"}),a(L,{title:"登录账号"})]),_:1},8,["active"]),m.value===0?(s(),S(x,{key:0},[a(N,{"label-position":"top"},{default:l(()=>[a(f,{label:"这台机器上的 ELLM 会被哪些设备访问?"},{default:l(()=>[a(F,{modelValue:n.value,"onUpdate:modelValue":e[0]||(e[0]=t=>n.value=t)},{default:l(()=>[a(g,{value:"local"},{default:l(()=>[...e[9]||(e[9]=[o("仅本机使用(本地个人部署)",-1)])]),_:1}),a(g,{value:"server"},{default:l(()=>[...e[10]||(e[10]=[o("局域网 / 公网可访问(服务器部署)",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1})]),_:1}),n.value==="server"?(s(),p(V,{key:0,type:"warning","show-icon":"",closable:!1,title:"服务器部署存在安全风险",description:"服务将暴露给网络中的其他设备:不设置主密钥,任何人都能消耗你的模型额度;不修改默认账号密码(admin/admin),陌生人可直接登录管理台查看供应商密钥与调用日志。后两步将强制要求设置。",class:"mb-16"})):n.value==="local"?(s(),p(V,{key:1,type:"info","show-icon":"",closable:!1,title:"本地模式",description:"仅本机可访问时,主密钥与登录账号均可在后续步骤跳过(保持 /v1 免鉴权与默认账号 admin/admin)。若日后开放给其他设备,请再回来补全。",class:"mb-16"})):K("",!0),k("div",ae,[e[12]||(e[12]=k("span",null,null,-1)),a(y,{type:"primary",disabled:!n.value,onClick:e[1]||(e[1]=t=>m.value=1)},{default:l(()=>[...e[11]||(e[11]=[o("下一步",-1)])]),_:1},8,["disabled"])])],64)):m.value===1?(s(),S(x,{key:1},[w.value?(s(),p(V,{key:0,type:"success","show-icon":"",closable:!1,title:"已检测到主密钥",description:"当前部署已设置主密钥(可在「设置」页查看修改),本步可直接跳过;如需更换可在下方重新生成或输入。",class:"mb-16"})):K("",!0),a(N,{"label-position":"top"},{default:l(()=>[a(f,{label:"主密钥(调用 /v1/* 接口的 Bearer Token)"},{default:l(()=>[a(F,{modelValue:d.value,"onUpdate:modelValue":e[2]||(e[2]=t=>d.value=t)},{default:l(()=>[a(g,{value:"generate"},{default:l(()=>[...e[13]||(e[13]=[o("随机生成(推荐)",-1)])]),_:1}),a(g,{value:"custom"},{default:l(()=>[...e[14]||(e[14]=[o("自定义输入",-1)])]),_:1}),T.value?(s(),p(g,{key:0,value:"skip"},{default:l(()=>[o("跳过"+I(R.value),1)]),_:1})):K("",!0)]),_:1},8,["modelValue"])]),_:1}),d.value==="generate"?(s(),S(x,{key:0},[b.value?(s(),p(f,{key:1,label:"请复制保存你的主密钥(设置页也可随时查看):"},{default:l(()=>[k("div",le,[a(C,{"model-value":b.value,readonly:""},{append:l(()=>[a(y,{onClick:Z},{default:l(()=>[...e[16]||(e[16]=[o("复制",-1)])]),_:1})]),_:1},8,["model-value"])])]),_:1})):(s(),p(f,{key:0},{default:l(()=>[a(y,{type:"primary",plain:"",onClick:H},{default:l(()=>[...e[15]||(e[15]=[o("生成随机密钥",-1)])]),_:1})]),_:1}))],64)):K("",!0),d.value==="custom"?(s(),p(f,{key:1,label:"自定义主密钥(至少 8 位)"},{default:l(()=>[a(C,{modelValue:M.value,"onUpdate:modelValue":e[3]||(e[3]=t=>M.value=t),placeholder:"例如 my-secret-key-2026","show-password":"",clearable:""},null,8,["modelValue"])]),_:1})):K("",!0)]),_:1}),k("div",te,[a(y,{onClick:e[4]||(e[4]=t=>m.value=0)},{default:l(()=>[...e[17]||(e[17]=[o("上一步",-1)])]),_:1}),a(y,{type:"primary",loading:B.value,disabled:!D.value,onClick:j},{default:l(()=>[...e[18]||(e[18]=[o(" 下一步 ",-1)])]),_:1},8,["loading","disabled"])])],64)):(s(),S(x,{key:2},[n.value==="server"?(s(),p(V,{key:0,type:"warning","show-icon":"",closable:!1,title:"必须设置登录账号密码",description:"服务器部署的管理台会暴露在局域网/公网中,默认账号 admin/admin 等于向所有人开放管理权限,请务必设置强密码。",class:"mb-16"})):(s(),p(V,{key:1,type:"info","show-icon":"",closable:!1,title:"可选步骤",description:"本地模式可跳过,保持默认账号 admin/admin。建议设置专属账号密码,避免日后开放访问时遗忘修改。",class:"mb-16"})),a(N,{"label-position":"top",onSubmit:W($,["prevent"])},{default:l(()=>[a(f,{label:"用户名"},{default:l(()=>[a(C,{modelValue:_.value,"onUpdate:modelValue":e[5]||(e[5]=t=>_.value=t),placeholder:"请输入用户名",clearable:""},null,8,["modelValue"])]),_:1}),a(f,{label:"密码(至少 6 位)"},{default:l(()=>[a(C,{modelValue:c.value,"onUpdate:modelValue":e[6]||(e[6]=t=>c.value=t),type:"password","show-password":"",placeholder:"请输入密码"},null,8,["modelValue"])]),_:1}),a(f,{label:"确认密码"},{default:l(()=>[a(C,{modelValue:U.value,"onUpdate:modelValue":e[7]||(e[7]=t=>U.value=t),type:"password","show-password":"",placeholder:"请再次输入密码"},null,8,["modelValue"])]),_:1})]),_:1}),k("div",se,[a(y,{onClick:e[8]||(e[8]=t=>m.value=1)},{default:l(()=>[...e[19]||(e[19]=[o("上一步",-1)])]),_:1}),a(y,{type:"primary",loading:E.value,onClick:$},{default:l(()=>[o(I(n.value==="server"||_.value||c.value?"完成":"跳过并完成"),1)]),_:1},8,["loading"])])],64))]),_:1})])}}}),ie=h(oe,[["__scopeId","data-v-89c00f65"]]);export{ie as default};
1
+ import{d as J,p as O,ab as Q,e as S,g as a,w as l,r as i,a as r,o as s,f as k,F as x,h as o,c as p,i as K,t as I,Z as W,l as z,A,aa as v,ac as X,m as Y,_ as h}from"./index-sPrBBnf5.js";const ee={class:"setup-wrap"},ae={class:"wizard-actions"},le={class:"key-row"},te={class:"wizard-actions"},se={class:"wizard-actions"},oe=J({__name:"SetupView",setup(ne){const P=Y(),m=i(0),n=i(""),d=i("generate"),b=i(""),M=i(""),_=i(""),c=i(""),U=i(""),B=i(!1),E=i(!1),w=i(!1);O(async()=>{const u=await Q();w.value=u.masterKeySet,w.value&&(d.value="skip")});const T=z(()=>n.value==="local"||w.value),R=z(()=>w.value?"(保留现有主密钥)":"(/v1 保持免鉴权)"),D=z(()=>d.value==="skip"?T.value:d.value==="custom"?M.value.trim().length>=8:b.value!=="");async function H(){try{const u=await A.post("/api/setup/master-key",{mode:"generate"});b.value=u.masterKey}catch(u){v.error(u.message||"生成失败")}}async function Z(){try{await navigator.clipboard.writeText(b.value),v.success("已复制到剪贴板")}catch{v.error("复制失败,请手动选择复制")}}async function j(){if(d.value==="skip"){m.value=2;return}B.value=!0;try{d.value==="custom"&&await A.post("/api/setup/master-key",{mode:"custom",masterKey:M.value.trim()}),w.value=!0,m.value=2}catch(u){v.error(u.message||"保存失败")}finally{B.value=!1}}async function $(){const u=_.value!==""||c.value!=="";if(u||n.value==="server"){if(!_.value.trim()){v.warning("请输入用户名");return}if(c.value.length<6){v.warning("密码至少 6 位");return}if(c.value!==U.value){v.warning("两次输入的密码不一致");return}}E.value=!0;try{await A.post("/api/setup/finish",{deployment:n.value,...u?{username:_.value.trim(),password:c.value}:{}}),X(n.value),v.success("初始化完成,请使用新账号登录"),await P.push("/login")}catch(e){v.error(e.message||"保存失败")}finally{E.value=!1}}return(u,e)=>{const L=r("el-step"),q=r("el-steps"),g=r("el-radio"),F=r("el-radio-group"),f=r("el-form-item"),N=r("el-form"),V=r("el-alert"),y=r("el-button"),C=r("el-input"),G=r("el-card");return s(),S("div",ee,[a(G,{shadow:"hover",class:"setup-card"},{default:l(()=>[e[20]||(e[20]=k("h2",{class:"setup-title"},"ELLM 初始化向导",-1)),a(q,{active:m.value,"align-center":"","finish-status":"success",class:"mb-24"},{default:l(()=>[a(L,{title:"部署环境"}),a(L,{title:"主密钥"}),a(L,{title:"登录账号"})]),_:1},8,["active"]),m.value===0?(s(),S(x,{key:0},[a(N,{"label-position":"top"},{default:l(()=>[a(f,{label:"这台机器上的 ELLM 会被哪些设备访问?"},{default:l(()=>[a(F,{modelValue:n.value,"onUpdate:modelValue":e[0]||(e[0]=t=>n.value=t)},{default:l(()=>[a(g,{value:"local"},{default:l(()=>[...e[9]||(e[9]=[o("仅本机使用(本地个人部署)",-1)])]),_:1}),a(g,{value:"server"},{default:l(()=>[...e[10]||(e[10]=[o("局域网 / 公网可访问(服务器部署)",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1})]),_:1}),n.value==="server"?(s(),p(V,{key:0,type:"warning","show-icon":"",closable:!1,title:"服务器部署存在安全风险",description:"服务将暴露给网络中的其他设备:不设置主密钥,任何人都能消耗你的模型额度;不修改默认账号密码(admin/admin),陌生人可直接登录管理台查看供应商密钥与调用日志。后两步将强制要求设置。",class:"mb-16"})):n.value==="local"?(s(),p(V,{key:1,type:"info","show-icon":"",closable:!1,title:"本地模式",description:"仅本机可访问时,主密钥与登录账号均可在后续步骤跳过(保持 /v1 免鉴权与默认账号 admin/admin)。若日后开放给其他设备,请再回来补全。",class:"mb-16"})):K("",!0),k("div",ae,[e[12]||(e[12]=k("span",null,null,-1)),a(y,{type:"primary",disabled:!n.value,onClick:e[1]||(e[1]=t=>m.value=1)},{default:l(()=>[...e[11]||(e[11]=[o("下一步",-1)])]),_:1},8,["disabled"])])],64)):m.value===1?(s(),S(x,{key:1},[w.value?(s(),p(V,{key:0,type:"success","show-icon":"",closable:!1,title:"已检测到主密钥",description:"当前部署已设置主密钥(可在「设置」页查看修改),本步可直接跳过;如需更换可在下方重新生成或输入。",class:"mb-16"})):K("",!0),a(N,{"label-position":"top"},{default:l(()=>[a(f,{label:"主密钥(调用 /v1/* 接口的 Bearer Token)"},{default:l(()=>[a(F,{modelValue:d.value,"onUpdate:modelValue":e[2]||(e[2]=t=>d.value=t)},{default:l(()=>[a(g,{value:"generate"},{default:l(()=>[...e[13]||(e[13]=[o("随机生成(推荐)",-1)])]),_:1}),a(g,{value:"custom"},{default:l(()=>[...e[14]||(e[14]=[o("自定义输入",-1)])]),_:1}),T.value?(s(),p(g,{key:0,value:"skip"},{default:l(()=>[o("跳过"+I(R.value),1)]),_:1})):K("",!0)]),_:1},8,["modelValue"])]),_:1}),d.value==="generate"?(s(),S(x,{key:0},[b.value?(s(),p(f,{key:1,label:"请复制保存你的主密钥(设置页也可随时查看):"},{default:l(()=>[k("div",le,[a(C,{"model-value":b.value,readonly:""},{append:l(()=>[a(y,{onClick:Z},{default:l(()=>[...e[16]||(e[16]=[o("复制",-1)])]),_:1})]),_:1},8,["model-value"])])]),_:1})):(s(),p(f,{key:0},{default:l(()=>[a(y,{type:"primary",plain:"",onClick:H},{default:l(()=>[...e[15]||(e[15]=[o("生成随机密钥",-1)])]),_:1})]),_:1}))],64)):K("",!0),d.value==="custom"?(s(),p(f,{key:1,label:"自定义主密钥(至少 8 位)"},{default:l(()=>[a(C,{modelValue:M.value,"onUpdate:modelValue":e[3]||(e[3]=t=>M.value=t),placeholder:"例如 my-secret-key-2026","show-password":"",clearable:""},null,8,["modelValue"])]),_:1})):K("",!0)]),_:1}),k("div",te,[a(y,{onClick:e[4]||(e[4]=t=>m.value=0)},{default:l(()=>[...e[17]||(e[17]=[o("上一步",-1)])]),_:1}),a(y,{type:"primary",loading:B.value,disabled:!D.value,onClick:j},{default:l(()=>[...e[18]||(e[18]=[o(" 下一步 ",-1)])]),_:1},8,["loading","disabled"])])],64)):(s(),S(x,{key:2},[n.value==="server"?(s(),p(V,{key:0,type:"warning","show-icon":"",closable:!1,title:"必须设置登录账号密码",description:"服务器部署的管理台会暴露在局域网/公网中,默认账号 admin/admin 等于向所有人开放管理权限,请务必设置强密码。",class:"mb-16"})):(s(),p(V,{key:1,type:"info","show-icon":"",closable:!1,title:"可选步骤",description:"本地模式可跳过,保持默认账号 admin/admin。建议设置专属账号密码,避免日后开放访问时遗忘修改。",class:"mb-16"})),a(N,{"label-position":"top",onSubmit:W($,["prevent"])},{default:l(()=>[a(f,{label:"用户名"},{default:l(()=>[a(C,{modelValue:_.value,"onUpdate:modelValue":e[5]||(e[5]=t=>_.value=t),placeholder:"请输入用户名",clearable:""},null,8,["modelValue"])]),_:1}),a(f,{label:"密码(至少 6 位)"},{default:l(()=>[a(C,{modelValue:c.value,"onUpdate:modelValue":e[6]||(e[6]=t=>c.value=t),type:"password","show-password":"",placeholder:"请输入密码"},null,8,["modelValue"])]),_:1}),a(f,{label:"确认密码"},{default:l(()=>[a(C,{modelValue:U.value,"onUpdate:modelValue":e[7]||(e[7]=t=>U.value=t),type:"password","show-password":"",placeholder:"请再次输入密码"},null,8,["modelValue"])]),_:1})]),_:1}),k("div",se,[a(y,{onClick:e[8]||(e[8]=t=>m.value=1)},{default:l(()=>[...e[19]||(e[19]=[o("上一步",-1)])]),_:1}),a(y,{type:"primary",loading:E.value,onClick:$},{default:l(()=>[o(I(n.value==="server"||_.value||c.value?"完成":"跳过并完成"),1)]),_:1},8,["loading"])])],64))]),_:1})])}}}),ie=h(oe,[["__scopeId","data-v-89c00f65"]]);export{ie as default};
@@ -1 +1 @@
1
- import{X as p,r as u,l as n,A as i}from"./index-X5PdkJlA.js";const y=p("config",()=>{const e=u({}),o=u(!1),t=n(()=>Array.isArray(e.value.providers)?e.value.providers:[]),l=n(()=>t.value.length),c=n(()=>t.value.reduce((a,d)=>{var s;return a+(((s=d.models)==null?void 0:s.length)??0)},0));async function f(){o.value=!0;try{e.value=await i.get("/api/config")}finally{o.value=!1}}async function r(){return await i.put("/api/config",e.value)}async function v(a){return a(e.value),await r()}return{config:e,loading:o,providers:t,providerCount:l,totalModelCount:c,load:f,save:r,update:v}});export{y as u};
1
+ import{X as p,r as u,l as n,A as i}from"./index-sPrBBnf5.js";const y=p("config",()=>{const e=u({}),o=u(!1),t=n(()=>Array.isArray(e.value.providers)?e.value.providers:[]),l=n(()=>t.value.length),c=n(()=>t.value.reduce((a,d)=>{var s;return a+(((s=d.models)==null?void 0:s.length)??0)},0));async function f(){o.value=!0;try{e.value=await i.get("/api/config")}finally{o.value=!1}}async function r(){return await i.put("/api/config",e.value)}async function v(a){return a(e.value),await r()}return{config:e,loading:o,providers:t,providerCount:l,totalModelCount:c,load:f,save:r,update:v}});export{y as u};
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/LayoutView-BePJfgia.js","assets/useManagerApi-C1_COxKg.js","assets/useToast-MtbkGJiM.js","assets/LayoutView-BUA8-IzK.css","assets/DashboardView-BCwrFrE2.js","assets/providers-H_9x778D.js","assets/config-CRWaUFQc.js","assets/DashboardView-BHG4xIdN.css","assets/ProvidersView-CYFs76wJ.js","assets/ProvidersView-Dpr8MI-p.css","assets/SettingsView-mPcaVAD2.js","assets/SettingsView-BkwyoOUb.css","assets/LogsView-Dyy_zkgg.js","assets/LogsView-Dl50XQhG.css","assets/ProcessView-CpPpMtFg.js","assets/ProcessView-DK0TvDpR.css","assets/LoginView-Dp7NmXtf.js","assets/LoginView-DrhfslIY.css","assets/SetupView-DX6S_KpK.js","assets/SetupView-kPJXJ7dX.css"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/LayoutView-BhN1f8wo.js","assets/useManagerApi-qPjxfB0X.js","assets/useToast-D76qyWZg.js","assets/LayoutView-CNd_MJt2.css","assets/DashboardView-3c_i_3VF.js","assets/providers-di7L8jy_.js","assets/config-nvSp8hHp.js","assets/DashboardView-BHG4xIdN.css","assets/ProvidersView-bE1Hv9D-.js","assets/ProvidersView-Dpr8MI-p.css","assets/SettingsView-BmLmC-_v.js","assets/SettingsView-Ceyj-ZDZ.css","assets/LogsView-2h2Y0XE8.js","assets/LogsView-Dl50XQhG.css","assets/ProcessView-Bs2k6aAf.js","assets/ProcessView-Bn8Ph7rh.css","assets/LoginView-J23vsMu2.js","assets/LoginView-DrhfslIY.css","assets/SetupView-Cgj7ubNz.js","assets/SetupView-kPJXJ7dX.css"])))=>i.map(i=>d[i]);
2
2
  (function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))a(l);new MutationObserver(l=>{for(const o of l)if(o.type==="childList")for(const r of o.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&a(r)}).observe(document,{childList:!0,subtree:!0});function n(l){const o={};return l.integrity&&(o.integrity=l.integrity),l.referrerPolicy&&(o.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?o.credentials="include":l.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function a(l){if(l.ep)return;l.ep=!0;const o=n(l);fetch(l.href,o)}})();/**
3
3
  * @vue/shared v3.5.42
4
4
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
@@ -93,4 +93,4 @@ usage: app.provide(ZINDEX_INJECTION_KEY, { current: 0 })`),{initialZIndex:a,curr
93
93
  * vue-router v4.6.4
94
94
  * (c) 2025 Eduardo San Martin Morote
95
95
  * @license MIT
96
- */let ufe=()=>location.protocol+"//"+location.host;function d9(e,t){const{pathname:n,search:a,hash:l}=t,o=e.indexOf("#");if(o>-1){let r=l.includes(e.slice(o))?e.slice(o).length:1,s=l.slice(r);return s[0]!=="/"&&(s="/"+s),c3(s,"")}return c3(n,e)+a+l}function cfe(e,t,n,a){let l=[],o=[],r=null;const s=({state:p})=>{const m=d9(e,location),v=n.value,h=t.value;let g=0;if(p){if(n.value=m,t.value=p,r&&r===v){r=null;return}g=h?p.position-h.position:0}else a(m);l.forEach(y=>{y(n.value,v,{delta:g,type:Y2.pop,direction:g?g>0?yp.forward:yp.back:yp.unknown})})};function u(){r=n.value}function c(p){l.push(p);const m=()=>{const v=l.indexOf(p);v>-1&&l.splice(v,1)};return o.push(m),m}function d(){if(document.visibilityState==="hidden"){const{history:p}=window;if(!p.state)return;p.replaceState(cn({},p.state,{scroll:y0()}),"")}}function f(){for(const p of o)p();o=[],window.removeEventListener("popstate",s),window.removeEventListener("pagehide",d),document.removeEventListener("visibilitychange",d)}return window.addEventListener("popstate",s),window.addEventListener("pagehide",d),document.addEventListener("visibilitychange",d),{pauseListeners:u,listen:c,destroy:f}}function v3(e,t,n,a=!1,l=!1){return{back:e,current:t,forward:n,replaced:a,position:window.history.length,scroll:l?y0():null}}function dfe(e){const{history:t,location:n}=window,a={value:d9(e,n)},l={value:t.state};l.value||o(a.value,{back:null,current:a.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function o(u,c,d){const f=e.indexOf("#"),p=f>-1?(n.host&&document.querySelector("base")?e:e.slice(f))+u:ufe()+e+u;try{t[d?"replaceState":"pushState"](c,"",p),l.value=c}catch(m){console.error(m),n[d?"replace":"assign"](p)}}function r(u,c){o(u,cn({},t.state,v3(l.value.back,u,l.value.forward,!0),c,{position:l.value.position}),!0),a.value=u}function s(u,c){const d=cn({},l.value,t.state,{forward:u,scroll:y0()});o(d.current,d,!0),o(u,cn({},v3(a.value,u,null),{position:d.position+1},c),!1),a.value=u}return{location:a,state:l,push:s,replace:r}}function ffe(e){e=Gde(e);const t=dfe(e),n=cfe(e,t.state,t.location,t.replace);function a(o,r=!0){r||n.pauseListeners(),history.go(o)}const l=cn({location:"",base:e,go:a,createHref:Zde.bind(null,e)},t,n);return Object.defineProperty(l,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(l,"state",{enumerable:!0,get:()=>t.state.value}),l}function pfe(e){return e=location.host?e||location.pathname+location.search:"",e.includes("#")||(e+="#"),ffe(e)}let Tr=(function(e){return e[e.Static=0]="Static",e[e.Param=1]="Param",e[e.Group=2]="Group",e})({});var Wn=(function(e){return e[e.Static=0]="Static",e[e.Param=1]="Param",e[e.ParamRegExp=2]="ParamRegExp",e[e.ParamRegExpEnd=3]="ParamRegExpEnd",e[e.EscapeNext=4]="EscapeNext",e})(Wn||{});const hfe={type:Tr.Static,value:""},vfe=/[a-zA-Z0-9_]/;function mfe(e){if(!e)return[[]];if(e==="/")return[[hfe]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(m){throw new Error(`ERR (${n})/"${c}": ${m}`)}let n=Wn.Static,a=n;const l=[];let o;function r(){o&&l.push(o),o=[]}let s=0,u,c="",d="";function f(){c&&(n===Wn.Static?o.push({type:Tr.Static,value:c}):n===Wn.Param||n===Wn.ParamRegExp||n===Wn.ParamRegExpEnd?(o.length>1&&(u==="*"||u==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),o.push({type:Tr.Param,value:c,regexp:d,repeatable:u==="*"||u==="+",optional:u==="*"||u==="?"})):t("Invalid state to consume buffer"),c="")}function p(){c+=u}for(;s<e.length;){if(u=e[s++],u==="\\"&&n!==Wn.ParamRegExp){a=n,n=Wn.EscapeNext;continue}switch(n){case Wn.Static:u==="/"?(c&&f(),r()):u===":"?(f(),n=Wn.Param):p();break;case Wn.EscapeNext:p(),n=a;break;case Wn.Param:u==="("?n=Wn.ParamRegExp:vfe.test(u)?p():(f(),n=Wn.Static,u!=="*"&&u!=="?"&&u!=="+"&&s--);break;case Wn.ParamRegExp:u===")"?d[d.length-1]=="\\"?d=d.slice(0,-1)+u:n=Wn.ParamRegExpEnd:d+=u;break;case Wn.ParamRegExpEnd:f(),n=Wn.Static,u!=="*"&&u!=="?"&&u!=="+"&&s--,d="";break;default:t("Unknown state");break}}return n===Wn.ParamRegExp&&t(`Unfinished custom RegExp for param "${c}"`),f(),r(),l}const m3="[^/]+?",gfe={sensitive:!1,strict:!1,start:!0,end:!0};var ga=(function(e){return e[e._multiplier=10]="_multiplier",e[e.Root=90]="Root",e[e.Segment=40]="Segment",e[e.SubSegment=30]="SubSegment",e[e.Static=40]="Static",e[e.Dynamic=20]="Dynamic",e[e.BonusCustomRegExp=10]="BonusCustomRegExp",e[e.BonusWildcard=-50]="BonusWildcard",e[e.BonusRepeatable=-20]="BonusRepeatable",e[e.BonusOptional=-8]="BonusOptional",e[e.BonusStrict=.7000000000000001]="BonusStrict",e[e.BonusCaseSensitive=.25]="BonusCaseSensitive",e})(ga||{});const yfe=/[.+*?^${}()[\]/\\]/g;function bfe(e,t){const n=cn({},gfe,t),a=[];let l=n.start?"^":"";const o=[];for(const c of e){const d=c.length?[]:[ga.Root];n.strict&&!c.length&&(l+="/");for(let f=0;f<c.length;f++){const p=c[f];let m=ga.Segment+(n.sensitive?ga.BonusCaseSensitive:0);if(p.type===Tr.Static)f||(l+="/"),l+=p.value.replace(yfe,"\\$&"),m+=ga.Static;else if(p.type===Tr.Param){const{value:v,repeatable:h,optional:g,regexp:y}=p;o.push({name:v,repeatable:h,optional:g});const b=y||m3;if(b!==m3){m+=ga.BonusCustomRegExp;try{`${b}`}catch(_){throw new Error(`Invalid custom RegExp for param "${v}" (${b}): `+_.message)}}let w=h?`((?:${b})(?:/(?:${b}))*)`:`(${b})`;f||(w=g&&c.length<2?`(?:/${w})`:"/"+w),g&&(w+="?"),l+=w,m+=ga.Dynamic,g&&(m+=ga.BonusOptional),h&&(m+=ga.BonusRepeatable),b===".*"&&(m+=ga.BonusWildcard)}d.push(m)}a.push(d)}if(n.strict&&n.end){const c=a.length-1;a[c][a[c].length-1]+=ga.BonusStrict}n.strict||(l+="/?"),n.end?l+="$":n.strict&&!l.endsWith("/")&&(l+="(?:/|$)");const r=new RegExp(l,n.sensitive?"":"i");function s(c){const d=c.match(r),f={};if(!d)return null;for(let p=1;p<d.length;p++){const m=d[p]||"",v=o[p-1];f[v.name]=m&&v.repeatable?m.split("/"):m}return f}function u(c){let d="",f=!1;for(const p of e){(!f||!d.endsWith("/"))&&(d+="/"),f=!1;for(const m of p)if(m.type===Tr.Static)d+=m.value;else if(m.type===Tr.Param){const{value:v,repeatable:h,optional:g}=m,y=v in c?c[v]:"";if(Cl(y)&&!h)throw new Error(`Provided param "${v}" is an array but it is not repeatable (* or + modifiers)`);const b=Cl(y)?y.join("/"):y;if(!b)if(g)p.length<2&&(d.endsWith("/")?d=d.slice(0,-1):f=!0);else throw new Error(`Missing required param "${v}"`);d+=b}}return d||"/"}return{re:r,score:a,keys:o,parse:s,stringify:u}}function wfe(e,t){let n=0;for(;n<e.length&&n<t.length;){const a=t[n]-e[n];if(a)return a;n++}return e.length<t.length?e.length===1&&e[0]===ga.Static+ga.Segment?-1:1:e.length>t.length?t.length===1&&t[0]===ga.Static+ga.Segment?1:-1:0}function f9(e,t){let n=0;const a=e.score,l=t.score;for(;n<a.length&&n<l.length;){const o=wfe(a[n],l[n]);if(o)return o;n++}if(Math.abs(l.length-a.length)===1){if(g3(a))return 1;if(g3(l))return-1}return l.length-a.length}function g3(e){const t=e[e.length-1];return e.length>0&&t[t.length-1]<0}const _fe={strict:!1,end:!0,sensitive:!1};function Cfe(e,t,n){const a=bfe(mfe(e.path),n),l=cn(a,{record:e,parent:t,children:[],alias:[]});return t&&!l.record.aliasOf==!t.record.aliasOf&&t.children.push(l),l}function Sfe(e,t){const n=[],a=new Map;t=u3(_fe,t);function l(f){return a.get(f)}function o(f,p,m){const v=!m,h=b3(f);h.aliasOf=m&&m.record;const g=u3(t,f),y=[h];if("alias"in f){const _=typeof f.alias=="string"?[f.alias]:f.alias;for(const C of _)y.push(b3(cn({},h,{components:m?m.record.components:h.components,path:C,aliasOf:m?m.record:h})))}let b,w;for(const _ of y){const{path:C}=_;if(p&&C[0]!=="/"){const k=p.record.path,T=k[k.length-1]==="/"?"":"/";_.path=p.record.path+(C&&T+C)}if(b=Cfe(_,p,g),m?m.alias.push(b):(w=w||b,w!==b&&w.alias.push(b),v&&f.name&&!w3(b)&&r(f.name)),p9(b)&&u(b),h.children){const k=h.children;for(let T=0;T<k.length;T++)o(k[T],b,m&&m.children[T])}m=m||b}return w?()=>{r(w)}:Ji}function r(f){if(u9(f)){const p=a.get(f);p&&(a.delete(f),n.splice(n.indexOf(p),1),p.children.forEach(r),p.alias.forEach(r))}else{const p=n.indexOf(f);p>-1&&(n.splice(p,1),f.record.name&&a.delete(f.record.name),f.children.forEach(r),f.alias.forEach(r))}}function s(){return n}function u(f){const p=Efe(f,n);n.splice(p,0,f),f.record.name&&!w3(f)&&a.set(f.record.name,f)}function c(f,p){let m,v={},h,g;if("name"in f&&f.name){if(m=a.get(f.name),!m)throw li(Rn.MATCHER_NOT_FOUND,{location:f});g=m.record.name,v=cn(y3(p.params,m.keys.filter(w=>!w.optional).concat(m.parent?m.parent.keys.filter(w=>w.optional):[]).map(w=>w.name)),f.params&&y3(f.params,m.keys.map(w=>w.name))),h=m.stringify(v)}else if(f.path!=null)h=f.path,m=n.find(w=>w.re.test(h)),m&&(v=m.parse(h),g=m.record.name);else{if(m=p.name?a.get(p.name):n.find(w=>w.re.test(p.path)),!m)throw li(Rn.MATCHER_NOT_FOUND,{location:f,currentLocation:p});g=m.record.name,v=cn({},p.params,f.params),h=m.stringify(v)}const y=[];let b=m;for(;b;)y.unshift(b.record),b=b.parent;return{name:g,path:h,params:v,matched:y,meta:kfe(y)}}e.forEach(f=>o(f));function d(){n.length=0,a.clear()}return{addRoute:o,resolve:c,removeRoute:r,clearRoutes:d,getRoutes:s,getRecordMatcher:l}}function y3(e,t){const n={};for(const a of t)a in e&&(n[a]=e[a]);return n}function b3(e){const t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:xfe(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}};return Object.defineProperty(t,"mods",{value:{}}),t}function xfe(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const a in e.components)t[a]=typeof n=="object"?n[a]:n;return t}function w3(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function kfe(e){return e.reduce((t,n)=>cn(t,n.meta),{})}function Efe(e,t){let n=0,a=t.length;for(;n!==a;){const o=n+a>>1;f9(e,t[o])<0?a=o:n=o+1}const l=Tfe(e);return l&&(a=t.lastIndexOf(l,a-1)),a}function Tfe(e){let t=e;for(;t=t.parent;)if(p9(t)&&f9(e,t)===0)return t}function p9({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function _3(e){const t=Le(b0),n=Le(f1),a=x(()=>{const u=i(e.to);return t.resolve(u)}),l=x(()=>{const{matched:u}=a.value,{length:c}=u,d=u[c-1],f=n.matched;if(!d||!f.length)return-1;const p=f.findIndex(ai.bind(null,d));if(p>-1)return p;const m=C3(u[c-2]);return c>1&&C3(d)===m&&f[f.length-1].path!==m?f.findIndex(ai.bind(null,u[c-2])):p}),o=x(()=>l.value>-1&&$fe(n.params,a.value.params)),r=x(()=>l.value>-1&&l.value===n.matched.length-1&&i9(n.params,a.value.params));function s(u={}){if(Afe(u)){const c=t[i(e.replace)?"replace":"push"](i(e.to)).catch(Ji);return e.viewTransition&&typeof document<"u"&&"startViewTransition"in document&&document.startViewTransition(()=>c),c}return Promise.resolve()}return{route:a,href:x(()=>a.value.href),isActive:o,isExactActive:r,navigate:s}}function Mfe(e){return e.length===1?e[0]:e}const Ofe=K({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"},viewTransition:Boolean},useLink:_3,setup(e,{slots:t}){const n=Dt(_3(e)),{options:a}=Le(b0),l=x(()=>({[S3(e.activeClass,a.linkActiveClass,"router-link-active")]:n.isActive,[S3(e.exactActiveClass,a.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const o=t.default&&Mfe(t.default(n));return e.custom?o:Ze("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:l.value},o)}}}),Nfe=Ofe;function Afe(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function $fe(e,t){for(const n in t){const a=t[n],l=e[n];if(typeof a=="string"){if(a!==l)return!1}else if(!Cl(l)||l.length!==a.length||a.some((o,r)=>o.valueOf()!==l[r].valueOf()))return!1}return!0}function C3(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const S3=(e,t,n)=>e??t??n,Rfe=K({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const a=Le(X2),l=x(()=>e.route||a.value),o=Le(h3,0),r=x(()=>{let c=i(o);const{matched:d}=l.value;let f;for(;(f=d[c])&&!f.components;)c++;return c}),s=x(()=>l.value.matched[r.value]);St(h3,x(()=>r.value+1)),St(sfe,s),St(X2,l);const u=B();return fe(()=>[u.value,s.value,e.name],([c,d,f],[p,m,v])=>{d&&(d.instances[f]=c,m&&m!==d&&c&&c===p&&(d.leaveGuards.size||(d.leaveGuards=m.leaveGuards),d.updateGuards.size||(d.updateGuards=m.updateGuards))),c&&d&&(!m||!ai(d,m)||!p)&&(d.enterCallbacks[f]||[]).forEach(h=>h(c))},{flush:"post"}),()=>{const c=l.value,d=e.name,f=s.value,p=f&&f.components[d];if(!p)return x3(n.default,{Component:p,route:c});const m=f.props[d],v=m?m===!0?c.params:typeof m=="function"?m(c):m:null,g=Ze(p,cn({},v,t,{onVnodeUnmounted:y=>{y.component.isUnmounted&&(f.instances[d]=null)},ref:u}));return x3(n.default,{Component:g,route:c})||g}}});function x3(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Pfe=Rfe;function Ife(e){const t=Sfe(e.routes,e),n=e.parseQuery||ofe,a=e.stringifyQuery||p3,l=e.history,o=Oi(),r=Oi(),s=Oi(),u=Et(zo);let c=zo;Ss&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const d=mp.bind(null,Q=>""+Q),f=mp.bind(null,Fde),p=mp.bind(null,$u);function m(Q,ce){let de,Y;return u9(Q)?(de=t.getRecordMatcher(Q),Y=ce):Y=Q,t.addRoute(Y,de)}function v(Q){const ce=t.getRecordMatcher(Q);ce&&t.removeRoute(ce)}function h(){return t.getRoutes().map(Q=>Q.record)}function g(Q){return!!t.getRecordMatcher(Q)}function y(Q,ce){if(ce=cn({},ce||u.value),typeof Q=="string"){const G=gp(n,Q,ce.path),se=t.resolve({path:G.path},ce),he=l.createHref(G.fullPath);return cn(G,se,{params:p(se.params),hash:$u(G.hash),redirectedFrom:void 0,href:he})}let de;if(Q.path!=null)de=cn({},Q,{path:gp(n,Q.path,ce.path).path});else{const G=cn({},Q.params);for(const se in G)G[se]==null&&delete G[se];de=cn({},Q,{params:f(G)}),ce.params=f(ce.params)}const Y=t.resolve(de,ce),Z=Q.hash||"";Y.params=d(p(Y.params));const ie=jde(a,cn({},Q,{hash:zde(Z),path:Y.path})),j=l.createHref(ie);return cn({fullPath:ie,hash:Z,query:a===p3?rfe(Q.query):Q.query||{}},Y,{redirectedFrom:void 0,href:j})}function b(Q){return typeof Q=="string"?gp(n,Q,u.value.path):cn({},Q)}function w(Q,ce){if(c!==Q)return li(Rn.NAVIGATION_CANCELLED,{from:ce,to:Q})}function _(Q){return T(Q)}function C(Q){return _(cn(b(Q),{replace:!0}))}function k(Q,ce){const de=Q.matched[Q.matched.length-1];if(de&&de.redirect){const{redirect:Y}=de;let Z=typeof Y=="function"?Y(Q,ce):Y;return typeof Z=="string"&&(Z=Z.includes("?")||Z.includes("#")?Z=b(Z):{path:Z},Z.params={}),cn({query:Q.query,hash:Q.hash,params:Z.path!=null?{}:Q.params},Z)}}function T(Q,ce){const de=c=y(Q),Y=u.value,Z=Q.state,ie=Q.force,j=Q.replace===!0,G=k(de,Y);if(G)return T(cn(b(G),{state:typeof G=="object"?cn({},Z,G.state):Z,force:ie,replace:j}),ce||de);const se=de;se.redirectedFrom=ce;let he;return!ie&&qde(a,Y,de)&&(he=li(Rn.NAVIGATION_DUPLICATED,{to:se,from:Y}),q(Y,Y,!0,!1)),(he?Promise.resolve(he):N(se,Y)).catch(ve=>oo(ve)?oo(ve,Rn.NAVIGATION_GUARD_REDIRECT)?ve:D(ve):V(ve,se,Y)).then(ve=>{if(ve){if(oo(ve,Rn.NAVIGATION_GUARD_REDIRECT))return T(cn({replace:j},b(ve.to),{state:typeof ve.to=="object"?cn({},Z,ve.to.state):Z,force:ie}),ce||se)}else ve=$(se,Y,!0,j,Z);return R(se,Y,ve),ve})}function E(Q,ce){const de=w(Q,ce);return de?Promise.reject(de):Promise.resolve()}function M(Q){const ce=ee.values().next().value;return ce&&typeof ce.runWithContext=="function"?ce.runWithContext(Q):Q()}function N(Q,ce){let de;const[Y,Z,ie]=ife(Q,ce);de=bp(Y.reverse(),"beforeRouteLeave",Q,ce);for(const G of Y)G.leaveGuards.forEach(se=>{de.push(Go(se,Q,ce))});const j=E.bind(null,Q,ce);return de.push(j),le(de).then(()=>{de=[];for(const G of o.list())de.push(Go(G,Q,ce));return de.push(j),le(de)}).then(()=>{de=bp(Z,"beforeRouteUpdate",Q,ce);for(const G of Z)G.updateGuards.forEach(se=>{de.push(Go(se,Q,ce))});return de.push(j),le(de)}).then(()=>{de=[];for(const G of ie)if(G.beforeEnter)if(Cl(G.beforeEnter))for(const se of G.beforeEnter)de.push(Go(se,Q,ce));else de.push(Go(G.beforeEnter,Q,ce));return de.push(j),le(de)}).then(()=>(Q.matched.forEach(G=>G.enterCallbacks={}),de=bp(ie,"beforeRouteEnter",Q,ce,M),de.push(j),le(de))).then(()=>{de=[];for(const G of r.list())de.push(Go(G,Q,ce));return de.push(j),le(de)}).catch(G=>oo(G,Rn.NAVIGATION_CANCELLED)?G:Promise.reject(G))}function R(Q,ce,de){s.list().forEach(Y=>M(()=>Y(Q,ce,de)))}function $(Q,ce,de,Y,Z){const ie=w(Q,ce);if(ie)return ie;const j=ce===zo,G=Ss?history.state:{};de&&(Y||j?l.replace(Q.fullPath,cn({scroll:j&&G&&G.scroll},Z)):l.push(Q.fullPath,Z)),u.value=Q,q(Q,ce,de,j),D()}let I;function W(){I||(I=l.listen((Q,ce,de)=>{if(!ne.listening)return;const Y=y(Q),Z=k(Y,ne.currentRoute.value);if(Z){T(cn(Z,{replace:!0,force:!0}),Y).catch(Ji);return}c=Y;const ie=u.value;Ss&&efe(f3(ie.fullPath,de.delta),y0()),N(Y,ie).catch(j=>oo(j,Rn.NAVIGATION_ABORTED|Rn.NAVIGATION_CANCELLED)?j:oo(j,Rn.NAVIGATION_GUARD_REDIRECT)?(T(cn(b(j.to),{force:!0}),Y).then(G=>{oo(G,Rn.NAVIGATION_ABORTED|Rn.NAVIGATION_DUPLICATED)&&!de.delta&&de.type===Y2.pop&&l.go(-1,!1)}).catch(Ji),Promise.reject()):(de.delta&&l.go(-de.delta,!1),V(j,Y,ie))).then(j=>{j=j||$(Y,ie,!1),j&&(de.delta&&!oo(j,Rn.NAVIGATION_CANCELLED)?l.go(-de.delta,!1):de.type===Y2.pop&&oo(j,Rn.NAVIGATION_ABORTED|Rn.NAVIGATION_DUPLICATED)&&l.go(-1,!1)),R(Y,ie,j)}).catch(Ji)}))}let H=Oi(),z=Oi(),L;function V(Q,ce,de){D(Q);const Y=z.list();return Y.length?Y.forEach(Z=>Z(Q,ce,de)):console.error(Q),Promise.reject(Q)}function F(){return L&&u.value!==zo?Promise.resolve():new Promise((Q,ce)=>{H.add([Q,ce])})}function D(Q){return L||(L=!Q,W(),H.list().forEach(([ce,de])=>Q?de(Q):ce()),H.reset()),Q}function q(Q,ce,de,Y){const{scrollBehavior:Z}=e;if(!Ss||!Z)return Promise.resolve();const ie=!de&&tfe(f3(Q.fullPath,0))||(Y||!de)&&history.state&&history.state.scroll||null;return Re().then(()=>Z(Q,ce,ie)).then(j=>j&&Qde(j)).catch(j=>V(j,Q,ce))}const U=Q=>l.go(Q);let te;const ee=new Set,ne={currentRoute:u,listening:!0,addRoute:m,removeRoute:v,clearRoutes:t.clearRoutes,hasRoute:g,getRoutes:h,resolve:y,options:e,push:_,replace:C,go:U,back:()=>U(-1),forward:()=>U(1),beforeEach:o.add,beforeResolve:r.add,afterEach:s.add,onError:z.add,isReady:F,install(Q){Q.component("RouterLink",Nfe),Q.component("RouterView",Pfe),Q.config.globalProperties.$router=ne,Object.defineProperty(Q.config.globalProperties,"$route",{enumerable:!0,get:()=>i(u)}),Ss&&!te&&u.value===zo&&(te=!0,_(l.location).catch(Y=>{}));const ce={};for(const Y in zo)Object.defineProperty(ce,Y,{get:()=>u.value[Y],enumerable:!0});Q.provide(b0,ne),Q.provide(f1,zu(ce)),Q.provide(X2,u);const de=Q.unmount;ee.add(Q),Q.unmount=function(){ee.delete(Q),ee.size<1&&(c=zo,I&&I(),I=null,u.value=zo,te=!1,L=!1),de()}}};function le(Q){return Q.reduce((ce,de)=>ce.then(()=>M(de)),Promise.resolve())}return ne}function qfe(){return Le(b0)}function Ufe(e){return Le(f1)}var k3;const Lfe=((k3=window.__ELLM_CONFIG__)==null?void 0:k3.API_BASE)??"",Vfe=3e4;let Ed=null;function Bfe(){return Ed===null&&(Ed=localStorage.getItem("webui_token")),Ed}function zfe(){Ed=null,localStorage.removeItem("webui_token"),p1.push("/login")}async function Zc(e,t,n){const a={"Content-Type":"application/json"},l=Bfe();l&&(a.Authorization=`Bearer ${l}`);const o=new AbortController,r=setTimeout(()=>o.abort(),Vfe);try{const s=await fetch(`${Lfe}${t}`,{method:e,headers:a,body:n!==void 0?JSON.stringify(n):void 0,signal:o.signal});if(s.status===401){const d=await s.text();let f={};try{f=d?JSON.parse(d):{}}catch{}throw t==="/api/auth/login"?new Error(f.error||"用户名或密码错误"):(zfe(),new Error(f.error||"未登录"))}const u=await s.text();let c;try{c=u?JSON.parse(u):{}}catch{throw new Error(`响应格式错误 (HTTP ${s.status})`)}if(!s.ok)throw new Error(c.error||`HTTP ${s.status}`);return c}catch(s){throw s.name==="AbortError"?new Error("请求超时"):s}finally{clearTimeout(r)}}const Dfe={get:e=>Zc("GET",e),post:(e,t)=>Zc("POST",e,t),put:(e,t)=>Zc("PUT",e,t),del:e=>Zc("DELETE",e)};let xs=null;async function Hfe(e=!1){if(xs&&!e)return xs;try{xs=await Dfe.get("/api/setup/status")}catch{xs={initialized:!0,masterKeySet:!0}}return xs}function Yfe(e){xs={initialized:!0,deployment:e,masterKeySet:!0}}const p1=Ife({history:pfe(),routes:[{path:"/",component:()=>Bo(()=>import("./LayoutView-BePJfgia.js"),__vite__mapDeps([0,1,2,3])),children:[{path:"",name:"Dashboard",component:()=>Bo(()=>import("./DashboardView-BCwrFrE2.js"),__vite__mapDeps([4,5,6,2,7]))},{path:"providers",name:"Providers",component:()=>Bo(()=>import("./ProvidersView-CYFs76wJ.js"),__vite__mapDeps([8,2,5,6,9]))},{path:"settings",name:"Settings",component:()=>Bo(()=>import("./SettingsView-mPcaVAD2.js"),__vite__mapDeps([10,2,6,1,11]))},{path:"logs",name:"Logs",component:()=>Bo(()=>import("./LogsView-Dyy_zkgg.js"),__vite__mapDeps([12,5,6,2,13]))},{path:"process",name:"Process",component:()=>Bo(()=>import("./ProcessView-CpPpMtFg.js"),__vite__mapDeps([14,1,2,15]))}]},{path:"/login",name:"Login",component:()=>Bo(()=>import("./LoginView-Dp7NmXtf.js"),__vite__mapDeps([16,17]))},{path:"/setup",name:"Setup",component:()=>Bo(()=>import("./SetupView-DX6S_KpK.js"),__vite__mapDeps([18,19]))},{path:"/:pathMatch(.*)*",redirect:"/"}]});p1.beforeEach(async e=>{if(!(await Hfe()).initialized)return e.name==="Setup"?!0:{name:"Setup"};if(e.name==="Setup")return{name:"Dashboard"};const n=localStorage.getItem("webui_token");if(e.name!=="Login"&&!n)return{name:"Login"}});const bc=cu(Ede);bc.use(pk());bc.use(_de,{locale:Cde});bc.use(p1);for(const[e,t]of Object.entries(iK))bc.component(e,t);bc.mount("#app");export{P as $,Dfe as A,y9 as B,Qr as C,en as D,ode as E,Fe as F,st as G,vo as H,ge as I,oi as J,er as K,K9 as L,D9 as M,dt as N,H9 as O,F9 as P,M3 as Q,Ra as R,A9 as S,jfe as T,Kfe as U,Ffe as V,to as W,Wfe as X,Dt as Y,Qe as Z,Sde as _,Kt as a,wD as a0,fe as a1,T8 as a2,rn as a3,lL as a4,OF as a5,_8 as a6,iv as a7,To as a8,jce as a9,Hfe as aa,Yfe as ab,ht as b,ue as c,K as d,A as e,O as f,J as g,bt as h,re as i,ph as j,i as k,x as l,qfe as m,Ufe as n,S as o,gt as p,_t as q,B as r,sn as s,Se as t,DS as u,Ie as v,ae as w,Z2 as x,Mn as y,$a as z};
96
+ */let ufe=()=>location.protocol+"//"+location.host;function d9(e,t){const{pathname:n,search:a,hash:l}=t,o=e.indexOf("#");if(o>-1){let r=l.includes(e.slice(o))?e.slice(o).length:1,s=l.slice(r);return s[0]!=="/"&&(s="/"+s),c3(s,"")}return c3(n,e)+a+l}function cfe(e,t,n,a){let l=[],o=[],r=null;const s=({state:p})=>{const m=d9(e,location),v=n.value,h=t.value;let g=0;if(p){if(n.value=m,t.value=p,r&&r===v){r=null;return}g=h?p.position-h.position:0}else a(m);l.forEach(y=>{y(n.value,v,{delta:g,type:Y2.pop,direction:g?g>0?yp.forward:yp.back:yp.unknown})})};function u(){r=n.value}function c(p){l.push(p);const m=()=>{const v=l.indexOf(p);v>-1&&l.splice(v,1)};return o.push(m),m}function d(){if(document.visibilityState==="hidden"){const{history:p}=window;if(!p.state)return;p.replaceState(cn({},p.state,{scroll:y0()}),"")}}function f(){for(const p of o)p();o=[],window.removeEventListener("popstate",s),window.removeEventListener("pagehide",d),document.removeEventListener("visibilitychange",d)}return window.addEventListener("popstate",s),window.addEventListener("pagehide",d),document.addEventListener("visibilitychange",d),{pauseListeners:u,listen:c,destroy:f}}function v3(e,t,n,a=!1,l=!1){return{back:e,current:t,forward:n,replaced:a,position:window.history.length,scroll:l?y0():null}}function dfe(e){const{history:t,location:n}=window,a={value:d9(e,n)},l={value:t.state};l.value||o(a.value,{back:null,current:a.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function o(u,c,d){const f=e.indexOf("#"),p=f>-1?(n.host&&document.querySelector("base")?e:e.slice(f))+u:ufe()+e+u;try{t[d?"replaceState":"pushState"](c,"",p),l.value=c}catch(m){console.error(m),n[d?"replace":"assign"](p)}}function r(u,c){o(u,cn({},t.state,v3(l.value.back,u,l.value.forward,!0),c,{position:l.value.position}),!0),a.value=u}function s(u,c){const d=cn({},l.value,t.state,{forward:u,scroll:y0()});o(d.current,d,!0),o(u,cn({},v3(a.value,u,null),{position:d.position+1},c),!1),a.value=u}return{location:a,state:l,push:s,replace:r}}function ffe(e){e=Gde(e);const t=dfe(e),n=cfe(e,t.state,t.location,t.replace);function a(o,r=!0){r||n.pauseListeners(),history.go(o)}const l=cn({location:"",base:e,go:a,createHref:Zde.bind(null,e)},t,n);return Object.defineProperty(l,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(l,"state",{enumerable:!0,get:()=>t.state.value}),l}function pfe(e){return e=location.host?e||location.pathname+location.search:"",e.includes("#")||(e+="#"),ffe(e)}let Tr=(function(e){return e[e.Static=0]="Static",e[e.Param=1]="Param",e[e.Group=2]="Group",e})({});var Wn=(function(e){return e[e.Static=0]="Static",e[e.Param=1]="Param",e[e.ParamRegExp=2]="ParamRegExp",e[e.ParamRegExpEnd=3]="ParamRegExpEnd",e[e.EscapeNext=4]="EscapeNext",e})(Wn||{});const hfe={type:Tr.Static,value:""},vfe=/[a-zA-Z0-9_]/;function mfe(e){if(!e)return[[]];if(e==="/")return[[hfe]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(m){throw new Error(`ERR (${n})/"${c}": ${m}`)}let n=Wn.Static,a=n;const l=[];let o;function r(){o&&l.push(o),o=[]}let s=0,u,c="",d="";function f(){c&&(n===Wn.Static?o.push({type:Tr.Static,value:c}):n===Wn.Param||n===Wn.ParamRegExp||n===Wn.ParamRegExpEnd?(o.length>1&&(u==="*"||u==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),o.push({type:Tr.Param,value:c,regexp:d,repeatable:u==="*"||u==="+",optional:u==="*"||u==="?"})):t("Invalid state to consume buffer"),c="")}function p(){c+=u}for(;s<e.length;){if(u=e[s++],u==="\\"&&n!==Wn.ParamRegExp){a=n,n=Wn.EscapeNext;continue}switch(n){case Wn.Static:u==="/"?(c&&f(),r()):u===":"?(f(),n=Wn.Param):p();break;case Wn.EscapeNext:p(),n=a;break;case Wn.Param:u==="("?n=Wn.ParamRegExp:vfe.test(u)?p():(f(),n=Wn.Static,u!=="*"&&u!=="?"&&u!=="+"&&s--);break;case Wn.ParamRegExp:u===")"?d[d.length-1]=="\\"?d=d.slice(0,-1)+u:n=Wn.ParamRegExpEnd:d+=u;break;case Wn.ParamRegExpEnd:f(),n=Wn.Static,u!=="*"&&u!=="?"&&u!=="+"&&s--,d="";break;default:t("Unknown state");break}}return n===Wn.ParamRegExp&&t(`Unfinished custom RegExp for param "${c}"`),f(),r(),l}const m3="[^/]+?",gfe={sensitive:!1,strict:!1,start:!0,end:!0};var ga=(function(e){return e[e._multiplier=10]="_multiplier",e[e.Root=90]="Root",e[e.Segment=40]="Segment",e[e.SubSegment=30]="SubSegment",e[e.Static=40]="Static",e[e.Dynamic=20]="Dynamic",e[e.BonusCustomRegExp=10]="BonusCustomRegExp",e[e.BonusWildcard=-50]="BonusWildcard",e[e.BonusRepeatable=-20]="BonusRepeatable",e[e.BonusOptional=-8]="BonusOptional",e[e.BonusStrict=.7000000000000001]="BonusStrict",e[e.BonusCaseSensitive=.25]="BonusCaseSensitive",e})(ga||{});const yfe=/[.+*?^${}()[\]/\\]/g;function bfe(e,t){const n=cn({},gfe,t),a=[];let l=n.start?"^":"";const o=[];for(const c of e){const d=c.length?[]:[ga.Root];n.strict&&!c.length&&(l+="/");for(let f=0;f<c.length;f++){const p=c[f];let m=ga.Segment+(n.sensitive?ga.BonusCaseSensitive:0);if(p.type===Tr.Static)f||(l+="/"),l+=p.value.replace(yfe,"\\$&"),m+=ga.Static;else if(p.type===Tr.Param){const{value:v,repeatable:h,optional:g,regexp:y}=p;o.push({name:v,repeatable:h,optional:g});const b=y||m3;if(b!==m3){m+=ga.BonusCustomRegExp;try{`${b}`}catch(_){throw new Error(`Invalid custom RegExp for param "${v}" (${b}): `+_.message)}}let w=h?`((?:${b})(?:/(?:${b}))*)`:`(${b})`;f||(w=g&&c.length<2?`(?:/${w})`:"/"+w),g&&(w+="?"),l+=w,m+=ga.Dynamic,g&&(m+=ga.BonusOptional),h&&(m+=ga.BonusRepeatable),b===".*"&&(m+=ga.BonusWildcard)}d.push(m)}a.push(d)}if(n.strict&&n.end){const c=a.length-1;a[c][a[c].length-1]+=ga.BonusStrict}n.strict||(l+="/?"),n.end?l+="$":n.strict&&!l.endsWith("/")&&(l+="(?:/|$)");const r=new RegExp(l,n.sensitive?"":"i");function s(c){const d=c.match(r),f={};if(!d)return null;for(let p=1;p<d.length;p++){const m=d[p]||"",v=o[p-1];f[v.name]=m&&v.repeatable?m.split("/"):m}return f}function u(c){let d="",f=!1;for(const p of e){(!f||!d.endsWith("/"))&&(d+="/"),f=!1;for(const m of p)if(m.type===Tr.Static)d+=m.value;else if(m.type===Tr.Param){const{value:v,repeatable:h,optional:g}=m,y=v in c?c[v]:"";if(Cl(y)&&!h)throw new Error(`Provided param "${v}" is an array but it is not repeatable (* or + modifiers)`);const b=Cl(y)?y.join("/"):y;if(!b)if(g)p.length<2&&(d.endsWith("/")?d=d.slice(0,-1):f=!0);else throw new Error(`Missing required param "${v}"`);d+=b}}return d||"/"}return{re:r,score:a,keys:o,parse:s,stringify:u}}function wfe(e,t){let n=0;for(;n<e.length&&n<t.length;){const a=t[n]-e[n];if(a)return a;n++}return e.length<t.length?e.length===1&&e[0]===ga.Static+ga.Segment?-1:1:e.length>t.length?t.length===1&&t[0]===ga.Static+ga.Segment?1:-1:0}function f9(e,t){let n=0;const a=e.score,l=t.score;for(;n<a.length&&n<l.length;){const o=wfe(a[n],l[n]);if(o)return o;n++}if(Math.abs(l.length-a.length)===1){if(g3(a))return 1;if(g3(l))return-1}return l.length-a.length}function g3(e){const t=e[e.length-1];return e.length>0&&t[t.length-1]<0}const _fe={strict:!1,end:!0,sensitive:!1};function Cfe(e,t,n){const a=bfe(mfe(e.path),n),l=cn(a,{record:e,parent:t,children:[],alias:[]});return t&&!l.record.aliasOf==!t.record.aliasOf&&t.children.push(l),l}function Sfe(e,t){const n=[],a=new Map;t=u3(_fe,t);function l(f){return a.get(f)}function o(f,p,m){const v=!m,h=b3(f);h.aliasOf=m&&m.record;const g=u3(t,f),y=[h];if("alias"in f){const _=typeof f.alias=="string"?[f.alias]:f.alias;for(const C of _)y.push(b3(cn({},h,{components:m?m.record.components:h.components,path:C,aliasOf:m?m.record:h})))}let b,w;for(const _ of y){const{path:C}=_;if(p&&C[0]!=="/"){const k=p.record.path,T=k[k.length-1]==="/"?"":"/";_.path=p.record.path+(C&&T+C)}if(b=Cfe(_,p,g),m?m.alias.push(b):(w=w||b,w!==b&&w.alias.push(b),v&&f.name&&!w3(b)&&r(f.name)),p9(b)&&u(b),h.children){const k=h.children;for(let T=0;T<k.length;T++)o(k[T],b,m&&m.children[T])}m=m||b}return w?()=>{r(w)}:Ji}function r(f){if(u9(f)){const p=a.get(f);p&&(a.delete(f),n.splice(n.indexOf(p),1),p.children.forEach(r),p.alias.forEach(r))}else{const p=n.indexOf(f);p>-1&&(n.splice(p,1),f.record.name&&a.delete(f.record.name),f.children.forEach(r),f.alias.forEach(r))}}function s(){return n}function u(f){const p=Efe(f,n);n.splice(p,0,f),f.record.name&&!w3(f)&&a.set(f.record.name,f)}function c(f,p){let m,v={},h,g;if("name"in f&&f.name){if(m=a.get(f.name),!m)throw li(Rn.MATCHER_NOT_FOUND,{location:f});g=m.record.name,v=cn(y3(p.params,m.keys.filter(w=>!w.optional).concat(m.parent?m.parent.keys.filter(w=>w.optional):[]).map(w=>w.name)),f.params&&y3(f.params,m.keys.map(w=>w.name))),h=m.stringify(v)}else if(f.path!=null)h=f.path,m=n.find(w=>w.re.test(h)),m&&(v=m.parse(h),g=m.record.name);else{if(m=p.name?a.get(p.name):n.find(w=>w.re.test(p.path)),!m)throw li(Rn.MATCHER_NOT_FOUND,{location:f,currentLocation:p});g=m.record.name,v=cn({},p.params,f.params),h=m.stringify(v)}const y=[];let b=m;for(;b;)y.unshift(b.record),b=b.parent;return{name:g,path:h,params:v,matched:y,meta:kfe(y)}}e.forEach(f=>o(f));function d(){n.length=0,a.clear()}return{addRoute:o,resolve:c,removeRoute:r,clearRoutes:d,getRoutes:s,getRecordMatcher:l}}function y3(e,t){const n={};for(const a of t)a in e&&(n[a]=e[a]);return n}function b3(e){const t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:xfe(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}};return Object.defineProperty(t,"mods",{value:{}}),t}function xfe(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const a in e.components)t[a]=typeof n=="object"?n[a]:n;return t}function w3(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function kfe(e){return e.reduce((t,n)=>cn(t,n.meta),{})}function Efe(e,t){let n=0,a=t.length;for(;n!==a;){const o=n+a>>1;f9(e,t[o])<0?a=o:n=o+1}const l=Tfe(e);return l&&(a=t.lastIndexOf(l,a-1)),a}function Tfe(e){let t=e;for(;t=t.parent;)if(p9(t)&&f9(e,t)===0)return t}function p9({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function _3(e){const t=Le(b0),n=Le(f1),a=x(()=>{const u=i(e.to);return t.resolve(u)}),l=x(()=>{const{matched:u}=a.value,{length:c}=u,d=u[c-1],f=n.matched;if(!d||!f.length)return-1;const p=f.findIndex(ai.bind(null,d));if(p>-1)return p;const m=C3(u[c-2]);return c>1&&C3(d)===m&&f[f.length-1].path!==m?f.findIndex(ai.bind(null,u[c-2])):p}),o=x(()=>l.value>-1&&$fe(n.params,a.value.params)),r=x(()=>l.value>-1&&l.value===n.matched.length-1&&i9(n.params,a.value.params));function s(u={}){if(Afe(u)){const c=t[i(e.replace)?"replace":"push"](i(e.to)).catch(Ji);return e.viewTransition&&typeof document<"u"&&"startViewTransition"in document&&document.startViewTransition(()=>c),c}return Promise.resolve()}return{route:a,href:x(()=>a.value.href),isActive:o,isExactActive:r,navigate:s}}function Mfe(e){return e.length===1?e[0]:e}const Ofe=K({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"},viewTransition:Boolean},useLink:_3,setup(e,{slots:t}){const n=Dt(_3(e)),{options:a}=Le(b0),l=x(()=>({[S3(e.activeClass,a.linkActiveClass,"router-link-active")]:n.isActive,[S3(e.exactActiveClass,a.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const o=t.default&&Mfe(t.default(n));return e.custom?o:Ze("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:l.value},o)}}}),Nfe=Ofe;function Afe(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function $fe(e,t){for(const n in t){const a=t[n],l=e[n];if(typeof a=="string"){if(a!==l)return!1}else if(!Cl(l)||l.length!==a.length||a.some((o,r)=>o.valueOf()!==l[r].valueOf()))return!1}return!0}function C3(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const S3=(e,t,n)=>e??t??n,Rfe=K({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const a=Le(X2),l=x(()=>e.route||a.value),o=Le(h3,0),r=x(()=>{let c=i(o);const{matched:d}=l.value;let f;for(;(f=d[c])&&!f.components;)c++;return c}),s=x(()=>l.value.matched[r.value]);St(h3,x(()=>r.value+1)),St(sfe,s),St(X2,l);const u=B();return fe(()=>[u.value,s.value,e.name],([c,d,f],[p,m,v])=>{d&&(d.instances[f]=c,m&&m!==d&&c&&c===p&&(d.leaveGuards.size||(d.leaveGuards=m.leaveGuards),d.updateGuards.size||(d.updateGuards=m.updateGuards))),c&&d&&(!m||!ai(d,m)||!p)&&(d.enterCallbacks[f]||[]).forEach(h=>h(c))},{flush:"post"}),()=>{const c=l.value,d=e.name,f=s.value,p=f&&f.components[d];if(!p)return x3(n.default,{Component:p,route:c});const m=f.props[d],v=m?m===!0?c.params:typeof m=="function"?m(c):m:null,g=Ze(p,cn({},v,t,{onVnodeUnmounted:y=>{y.component.isUnmounted&&(f.instances[d]=null)},ref:u}));return x3(n.default,{Component:g,route:c})||g}}});function x3(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Pfe=Rfe;function Ife(e){const t=Sfe(e.routes,e),n=e.parseQuery||ofe,a=e.stringifyQuery||p3,l=e.history,o=Oi(),r=Oi(),s=Oi(),u=Et(zo);let c=zo;Ss&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const d=mp.bind(null,Q=>""+Q),f=mp.bind(null,Fde),p=mp.bind(null,$u);function m(Q,ce){let de,Y;return u9(Q)?(de=t.getRecordMatcher(Q),Y=ce):Y=Q,t.addRoute(Y,de)}function v(Q){const ce=t.getRecordMatcher(Q);ce&&t.removeRoute(ce)}function h(){return t.getRoutes().map(Q=>Q.record)}function g(Q){return!!t.getRecordMatcher(Q)}function y(Q,ce){if(ce=cn({},ce||u.value),typeof Q=="string"){const G=gp(n,Q,ce.path),se=t.resolve({path:G.path},ce),he=l.createHref(G.fullPath);return cn(G,se,{params:p(se.params),hash:$u(G.hash),redirectedFrom:void 0,href:he})}let de;if(Q.path!=null)de=cn({},Q,{path:gp(n,Q.path,ce.path).path});else{const G=cn({},Q.params);for(const se in G)G[se]==null&&delete G[se];de=cn({},Q,{params:f(G)}),ce.params=f(ce.params)}const Y=t.resolve(de,ce),Z=Q.hash||"";Y.params=d(p(Y.params));const ie=jde(a,cn({},Q,{hash:zde(Z),path:Y.path})),j=l.createHref(ie);return cn({fullPath:ie,hash:Z,query:a===p3?rfe(Q.query):Q.query||{}},Y,{redirectedFrom:void 0,href:j})}function b(Q){return typeof Q=="string"?gp(n,Q,u.value.path):cn({},Q)}function w(Q,ce){if(c!==Q)return li(Rn.NAVIGATION_CANCELLED,{from:ce,to:Q})}function _(Q){return T(Q)}function C(Q){return _(cn(b(Q),{replace:!0}))}function k(Q,ce){const de=Q.matched[Q.matched.length-1];if(de&&de.redirect){const{redirect:Y}=de;let Z=typeof Y=="function"?Y(Q,ce):Y;return typeof Z=="string"&&(Z=Z.includes("?")||Z.includes("#")?Z=b(Z):{path:Z},Z.params={}),cn({query:Q.query,hash:Q.hash,params:Z.path!=null?{}:Q.params},Z)}}function T(Q,ce){const de=c=y(Q),Y=u.value,Z=Q.state,ie=Q.force,j=Q.replace===!0,G=k(de,Y);if(G)return T(cn(b(G),{state:typeof G=="object"?cn({},Z,G.state):Z,force:ie,replace:j}),ce||de);const se=de;se.redirectedFrom=ce;let he;return!ie&&qde(a,Y,de)&&(he=li(Rn.NAVIGATION_DUPLICATED,{to:se,from:Y}),q(Y,Y,!0,!1)),(he?Promise.resolve(he):N(se,Y)).catch(ve=>oo(ve)?oo(ve,Rn.NAVIGATION_GUARD_REDIRECT)?ve:D(ve):V(ve,se,Y)).then(ve=>{if(ve){if(oo(ve,Rn.NAVIGATION_GUARD_REDIRECT))return T(cn({replace:j},b(ve.to),{state:typeof ve.to=="object"?cn({},Z,ve.to.state):Z,force:ie}),ce||se)}else ve=$(se,Y,!0,j,Z);return R(se,Y,ve),ve})}function E(Q,ce){const de=w(Q,ce);return de?Promise.reject(de):Promise.resolve()}function M(Q){const ce=ee.values().next().value;return ce&&typeof ce.runWithContext=="function"?ce.runWithContext(Q):Q()}function N(Q,ce){let de;const[Y,Z,ie]=ife(Q,ce);de=bp(Y.reverse(),"beforeRouteLeave",Q,ce);for(const G of Y)G.leaveGuards.forEach(se=>{de.push(Go(se,Q,ce))});const j=E.bind(null,Q,ce);return de.push(j),le(de).then(()=>{de=[];for(const G of o.list())de.push(Go(G,Q,ce));return de.push(j),le(de)}).then(()=>{de=bp(Z,"beforeRouteUpdate",Q,ce);for(const G of Z)G.updateGuards.forEach(se=>{de.push(Go(se,Q,ce))});return de.push(j),le(de)}).then(()=>{de=[];for(const G of ie)if(G.beforeEnter)if(Cl(G.beforeEnter))for(const se of G.beforeEnter)de.push(Go(se,Q,ce));else de.push(Go(G.beforeEnter,Q,ce));return de.push(j),le(de)}).then(()=>(Q.matched.forEach(G=>G.enterCallbacks={}),de=bp(ie,"beforeRouteEnter",Q,ce,M),de.push(j),le(de))).then(()=>{de=[];for(const G of r.list())de.push(Go(G,Q,ce));return de.push(j),le(de)}).catch(G=>oo(G,Rn.NAVIGATION_CANCELLED)?G:Promise.reject(G))}function R(Q,ce,de){s.list().forEach(Y=>M(()=>Y(Q,ce,de)))}function $(Q,ce,de,Y,Z){const ie=w(Q,ce);if(ie)return ie;const j=ce===zo,G=Ss?history.state:{};de&&(Y||j?l.replace(Q.fullPath,cn({scroll:j&&G&&G.scroll},Z)):l.push(Q.fullPath,Z)),u.value=Q,q(Q,ce,de,j),D()}let I;function W(){I||(I=l.listen((Q,ce,de)=>{if(!ne.listening)return;const Y=y(Q),Z=k(Y,ne.currentRoute.value);if(Z){T(cn(Z,{replace:!0,force:!0}),Y).catch(Ji);return}c=Y;const ie=u.value;Ss&&efe(f3(ie.fullPath,de.delta),y0()),N(Y,ie).catch(j=>oo(j,Rn.NAVIGATION_ABORTED|Rn.NAVIGATION_CANCELLED)?j:oo(j,Rn.NAVIGATION_GUARD_REDIRECT)?(T(cn(b(j.to),{force:!0}),Y).then(G=>{oo(G,Rn.NAVIGATION_ABORTED|Rn.NAVIGATION_DUPLICATED)&&!de.delta&&de.type===Y2.pop&&l.go(-1,!1)}).catch(Ji),Promise.reject()):(de.delta&&l.go(-de.delta,!1),V(j,Y,ie))).then(j=>{j=j||$(Y,ie,!1),j&&(de.delta&&!oo(j,Rn.NAVIGATION_CANCELLED)?l.go(-de.delta,!1):de.type===Y2.pop&&oo(j,Rn.NAVIGATION_ABORTED|Rn.NAVIGATION_DUPLICATED)&&l.go(-1,!1)),R(Y,ie,j)}).catch(Ji)}))}let H=Oi(),z=Oi(),L;function V(Q,ce,de){D(Q);const Y=z.list();return Y.length?Y.forEach(Z=>Z(Q,ce,de)):console.error(Q),Promise.reject(Q)}function F(){return L&&u.value!==zo?Promise.resolve():new Promise((Q,ce)=>{H.add([Q,ce])})}function D(Q){return L||(L=!Q,W(),H.list().forEach(([ce,de])=>Q?de(Q):ce()),H.reset()),Q}function q(Q,ce,de,Y){const{scrollBehavior:Z}=e;if(!Ss||!Z)return Promise.resolve();const ie=!de&&tfe(f3(Q.fullPath,0))||(Y||!de)&&history.state&&history.state.scroll||null;return Re().then(()=>Z(Q,ce,ie)).then(j=>j&&Qde(j)).catch(j=>V(j,Q,ce))}const U=Q=>l.go(Q);let te;const ee=new Set,ne={currentRoute:u,listening:!0,addRoute:m,removeRoute:v,clearRoutes:t.clearRoutes,hasRoute:g,getRoutes:h,resolve:y,options:e,push:_,replace:C,go:U,back:()=>U(-1),forward:()=>U(1),beforeEach:o.add,beforeResolve:r.add,afterEach:s.add,onError:z.add,isReady:F,install(Q){Q.component("RouterLink",Nfe),Q.component("RouterView",Pfe),Q.config.globalProperties.$router=ne,Object.defineProperty(Q.config.globalProperties,"$route",{enumerable:!0,get:()=>i(u)}),Ss&&!te&&u.value===zo&&(te=!0,_(l.location).catch(Y=>{}));const ce={};for(const Y in zo)Object.defineProperty(ce,Y,{get:()=>u.value[Y],enumerable:!0});Q.provide(b0,ne),Q.provide(f1,zu(ce)),Q.provide(X2,u);const de=Q.unmount;ee.add(Q),Q.unmount=function(){ee.delete(Q),ee.size<1&&(c=zo,I&&I(),I=null,u.value=zo,te=!1,L=!1),de()}}};function le(Q){return Q.reduce((ce,de)=>ce.then(()=>M(de)),Promise.resolve())}return ne}function qfe(){return Le(b0)}function Ufe(e){return Le(f1)}var k3;const Lfe=((k3=window.__ELLM_CONFIG__)==null?void 0:k3.API_BASE)??"",Vfe=3e4;let Ed=null;function Bfe(){return Ed===null&&(Ed=localStorage.getItem("webui_token")),Ed}function zfe(){Ed=null,localStorage.removeItem("webui_token"),p1.push("/login")}async function Zc(e,t,n){const a={"Content-Type":"application/json"},l=Bfe();l&&(a.Authorization=`Bearer ${l}`);const o=new AbortController,r=setTimeout(()=>o.abort(),Vfe);try{const s=await fetch(`${Lfe}${t}`,{method:e,headers:a,body:n!==void 0?JSON.stringify(n):void 0,signal:o.signal});if(s.status===401){const d=await s.text();let f={};try{f=d?JSON.parse(d):{}}catch{}throw t==="/api/auth/login"?new Error(f.error||"用户名或密码错误"):(zfe(),new Error(f.error||"未登录"))}const u=await s.text();let c;try{c=u?JSON.parse(u):{}}catch{throw new Error(`响应格式错误 (HTTP ${s.status})`)}if(!s.ok)throw new Error(c.error||`HTTP ${s.status}`);return c}catch(s){throw s.name==="AbortError"?new Error("请求超时"):s}finally{clearTimeout(r)}}const Dfe={get:e=>Zc("GET",e),post:(e,t)=>Zc("POST",e,t),put:(e,t)=>Zc("PUT",e,t),del:e=>Zc("DELETE",e)};let xs=null;async function Hfe(e=!1){if(xs&&!e)return xs;try{xs=await Dfe.get("/api/setup/status")}catch{xs={initialized:!0,masterKeySet:!0}}return xs}function Yfe(e){xs={initialized:!0,deployment:e,masterKeySet:!0}}const p1=Ife({history:pfe(),routes:[{path:"/",component:()=>Bo(()=>import("./LayoutView-BhN1f8wo.js"),__vite__mapDeps([0,1,2,3])),children:[{path:"",name:"Dashboard",component:()=>Bo(()=>import("./DashboardView-3c_i_3VF.js"),__vite__mapDeps([4,5,6,2,7]))},{path:"providers",name:"Providers",component:()=>Bo(()=>import("./ProvidersView-bE1Hv9D-.js"),__vite__mapDeps([8,2,5,6,9]))},{path:"settings",name:"Settings",component:()=>Bo(()=>import("./SettingsView-BmLmC-_v.js"),__vite__mapDeps([10,2,6,1,11]))},{path:"logs",name:"Logs",component:()=>Bo(()=>import("./LogsView-2h2Y0XE8.js"),__vite__mapDeps([12,5,6,2,13]))},{path:"process",name:"Process",component:()=>Bo(()=>import("./ProcessView-Bs2k6aAf.js"),__vite__mapDeps([14,1,2,15]))}]},{path:"/login",name:"Login",component:()=>Bo(()=>import("./LoginView-J23vsMu2.js"),__vite__mapDeps([16,17]))},{path:"/setup",name:"Setup",component:()=>Bo(()=>import("./SetupView-Cgj7ubNz.js"),__vite__mapDeps([18,19]))},{path:"/:pathMatch(.*)*",redirect:"/"}]});p1.beforeEach(async e=>{if(!(await Hfe()).initialized)return e.name==="Setup"?!0:{name:"Setup"};if(e.name==="Setup")return{name:"Dashboard"};const n=localStorage.getItem("webui_token");if(e.name!=="Login"&&!n)return{name:"Login"}});const bc=cu(Ede);bc.use(pk());bc.use(_de,{locale:Cde});bc.use(p1);for(const[e,t]of Object.entries(iK))bc.component(e,t);bc.mount("#app");export{P as $,Dfe as A,y9 as B,Qr as C,en as D,ode as E,Fe as F,st as G,vo as H,ge as I,oi as J,er as K,K9 as L,D9 as M,dt as N,H9 as O,F9 as P,M3 as Q,Ra as R,A9 as S,jfe as T,Kfe as U,Ffe as V,to as W,Wfe as X,Dt as Y,Qe as Z,Sde as _,Kt as a,wD as a0,fe as a1,T8 as a2,rn as a3,lL as a4,OF as a5,_8 as a6,iv as a7,To as a8,p1 as a9,jce as aa,Hfe as ab,Yfe as ac,ht as b,ue as c,K as d,A as e,O as f,J as g,bt as h,re as i,ph as j,i as k,x as l,qfe as m,Ufe as n,S as o,gt as p,_t as q,B as r,sn as s,Se as t,DS as u,Ie as v,ae as w,Z2 as x,Mn as y,$a as z};
@@ -1 +1 @@
1
- import{X as w,r as u,l as i}from"./index-X5PdkJlA.js";import{u as C}from"./config-CRWaUFQc.js";const J=w("providers",()=>{const s=C(),c=u(!1),n=i(()=>s.providers),v=i(()=>s.loading),f=i(()=>n.value.length),m=i(()=>n.value.reduce((e,o)=>{var r;return e+(((r=o.models)==null?void 0:r.length)??0)},0)),t=u([]);function p(){try{const e=localStorage.getItem("provider_order");e&&(t.value=JSON.parse(e))}catch{}}function d(){try{localStorage.setItem("provider_order",JSON.stringify(t.value))}catch{}}const g=i(()=>{const e=t.value.map(r=>n.value.find(a=>a.name===r)).filter(r=>!!r);for(const r of n.value)e.find(a=>a.name===r.name)||e.push(r);const o=e.map(r=>r.name);return(o.length!==t.value.length||o.some((r,a)=>t.value[a]!==r))&&(t.value=o,d()),e});function S(e){t.value=e.map(o=>o.name),d()}function h(e){t.value=[e,...t.value.filter(o=>o!==e)],d()}async function y(){p(),await s.load()}async function l(e){return s.config.providers=e,await s.save()}async function P(e){const o=n.value.filter(r=>r.name!==e.name);return await l(o)}async function O(e){const o=n.value.map(r=>({...r,models:(r.models??[]).filter(a=>a.modelName!==e)}));return await l(o)}return{suppliers:n,loading:v,restarting:c,orderedProviders:g,providerCount:f,totalModelCount:m,loadProviders:y,saveProviders:l,deleteProvider:P,deleteModel:O,reorderProviders:S,moveProviderToTop:h}});export{J as u};
1
+ import{X as w,r as u,l as i}from"./index-sPrBBnf5.js";import{u as C}from"./config-nvSp8hHp.js";const J=w("providers",()=>{const s=C(),c=u(!1),n=i(()=>s.providers),v=i(()=>s.loading),f=i(()=>n.value.length),m=i(()=>n.value.reduce((e,o)=>{var r;return e+(((r=o.models)==null?void 0:r.length)??0)},0)),t=u([]);function p(){try{const e=localStorage.getItem("provider_order");e&&(t.value=JSON.parse(e))}catch{}}function d(){try{localStorage.setItem("provider_order",JSON.stringify(t.value))}catch{}}const g=i(()=>{const e=t.value.map(r=>n.value.find(a=>a.name===r)).filter(r=>!!r);for(const r of n.value)e.find(a=>a.name===r.name)||e.push(r);const o=e.map(r=>r.name);return(o.length!==t.value.length||o.some((r,a)=>t.value[a]!==r))&&(t.value=o,d()),e});function S(e){t.value=e.map(o=>o.name),d()}function h(e){t.value=[e,...t.value.filter(o=>o!==e)],d()}async function y(){p(),await s.load()}async function l(e){return s.config.providers=e,await s.save()}async function P(e){const o=n.value.filter(r=>r.name!==e.name);return await l(o)}async function O(e){const o=n.value.map(r=>({...r,models:(r.models??[]).filter(a=>a.modelName!==e)}));return await l(o)}return{suppliers:n,loading:v,restarting:c,orderedProviders:g,providerCount:f,totalModelCount:m,loadProviders:y,saveProviders:l,deleteProvider:P,deleteModel:O,reorderProviders:S,moveProviderToTop:h}});export{J as u};
@@ -0,0 +1 @@
1
+ import{a9 as w}from"./index-sPrBBnf5.js";var T;const h=((T=window.__ELLM_CONFIG__)==null?void 0:T.API_BASE)??"",f=3e4;let u=null;function E(){return u===null&&(u=localStorage.getItem("webui_token")),u}function S(){u=null,localStorage.removeItem("webui_token"),w.push("/login")}async function e(t,c,n,p=f){const i={"Content-Type":"application/json"},a=E();a&&(i.Authorization=`Bearer ${a}`);const s=new AbortController,o=setTimeout(()=>s.abort(),p);try{const r=await fetch(`${h}${c}`,{method:t,headers:i,body:n!==void 0?JSON.stringify(n):void 0,signal:s.signal});if(r.status===401)throw S(),new Error("未登录");const d=await r.text();let l;try{l=d?JSON.parse(d):{}}catch{throw new Error(`响应格式错误 (HTTP ${r.status})`)}if(!r.ok||l.ok===!1)throw new Error(l.error||`HTTP ${r.status}`);return l}catch(r){throw r.name==="AbortError"?new Error("请求超时"):r instanceof TypeError?new Error("服务不可达(重启窗口期或未启动,请稍后重试)"):r}finally{clearTimeout(o)}}const P={status:()=>e("GET","/status"),processes:()=>e("GET","/processes").then(t=>t.processes),start:()=>e("POST","/start"),stop:()=>e("POST","/stop"),restart:()=>e("POST","/restart"),autostart:()=>e("GET","/autostart"),setAutostart:t=>e("POST","/autostart",{enabled:t}),update:()=>e("GET","/update"),setUpdateEnabled:t=>e("POST","/update/enabled",{enabled:t}),checkUpdate:()=>e("POST","/update/check"),applyUpdate:()=>e("POST","/update/apply"),harness:()=>e("GET","/harness"),setClaudeCode:t=>e("POST","/harness/claude-code",{enabled:t}),setCodex:t=>e("POST","/harness/codex",{enabled:t}),setDsh:t=>e("POST","/harness/dsh",{enabled:t})};async function A(t){const{targetVersion:c,sinceAppliedAt:n,timeoutMs:p=600*1e3,pollMs:i=3e3}=t,a=Date.now()+p;for(;;){if(Date.now()>=a)return null;await new Promise(r=>setTimeout(r,i));let s=null;try{s=await P.update()}catch{continue}const o=s.lastApply;if(o&&o.toVersion===c&&(o.appliedAt??0)>(n??0))return o;if(Date.now()>=a)return null}}export{P as m,A as w};
@@ -0,0 +1 @@
1
+ import{aa as o}from"./index-sPrBBnf5.js";function u(){function s(t,a="success"){o({message:t,type:a,duration:3e3})}return{toast:s}}export{u};
@@ -1,10 +1,7 @@
1
1
  // web-ui 运行时配置(部署后可直接改这个文件,无需重新构建)
2
- // 标准形态:manager 管理服务托管本目录(http://127.0.0.1:<主端口+1>/),两项留空即全同源,无需修改。
3
- // API_BASE:管理台 API(/api/*)的基地址。留空 = 同源(manager 已反代 /api/* 到主服务);
4
- // 仅当把 dist 部署到其他静态服务器时填写,如 "http://192.168.1.10:29770"(backend 全局 CORS 已开)。
5
- // MANAGER_BASE:进程/代理开关接口(/status /restart /harness…)的基地址。
6
- // 留空 = 同源(manager 托管形态);独立部署且需进程管理页时填 manager 地址,如 "http://127.0.0.1:29771"。
2
+ // 标准形态:服务进程托管本目录(http://127.0.0.1:<port>/),API_BASE 留空即全同源,无需修改。
3
+ // API_BASE:管理台 API 与管理域接口(/api/*、/status、/harness…)的基地址。
4
+ // 留空 = 同源;仅当把 dist 部署到其他静态服务器时填写,如 "http://192.168.1.10:29770"
7
5
  window.__ELLM_CONFIG__ = {
8
6
  API_BASE: "",
9
- MANAGER_BASE: "",
10
7
  };
@@ -6,7 +6,7 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>ELLM - 网关管理台</title>
8
8
  <script src="/config.js"></script>
9
- <script type="module" crossorigin src="/assets/index-X5PdkJlA.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-sPrBBnf5.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-BecfPmag.css">
11
11
  </head>
12
12
  <body>
package/docs/DESIGN.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## 1. 背景与目标
6
6
 
7
- `ellm` monorepo(`gateway` 协议转换层 / `backend` 管理台服务 / `web-ui` Vue 前端)原为纯本地工程,目标是把 backend 单进程应用(内嵌网关 + 管理台 API)改造为可发布的 npm 包 **`ellm-proxy`**(2026-09-03 起 web-ui 由 manager 管理服务托管,backend 不再托管):
7
+ `ellm` monorepo(`gateway` 协议转换层 / `server` 管理台服务,原名 backend / `web-ui` Vue 前端)原为纯本地工程,目标是把 backend 单进程应用(内嵌网关 + 管理台 API)改造为可发布的 npm 包 **`ellm-proxy`**(2026-09-03 起 web-ui 由 manager 管理服务托管,backend 不再托管):
8
8
 
9
9
  ```bash
10
10
  npm i -g ellm-proxy
@@ -28,6 +28,7 @@ ellm-proxy stop # 停止后台服务
28
28
  | 10 | 默认端口 | 3000 太常用易冲突 → 随机定为 **29770**(避开 49152+ 系统动态端口区) |
29
29
  | 11 | 不内嵌 node.exe | 服务要求的 Node ≥ 22.13.0 由 CLI 解析为 pm2 `interpreter`:CLI 自身 node 满足则用 `process.execPath`,否则经 **get-node**(随包依赖)解析/下载(缓存命中零下载);pm2 daemon 的 node 版本无关紧要 |
30
30
  | 12 | web-ui 移交 manager 托管(2026-09-03) | backend 移除 `/web-ui` 静态托管(删 `webui-static.ts`);web-ui 改由 manager 管理服务托管:根路径静态资源 + 反代 `/api/*` 到主服务(端口每请求解析,主服务顺延漂移无感知),页面全接口同源零配置(`config.js` 的 `API_BASE`/`MANAGER_BASE` 留空);发布包恢复捆绑 `dist/web-ui/` 供 manager 定位(`ELLM_WEBUI_DIR` 可选覆盖,CLI 不再注入);manager 仍仅监听 127.0.0.1 |
31
+ | 13 | 去 pm2 + 单进程单端口(2026-09-06) | 详见 §6。删除 pm2/get-node 依赖与 manager/ 目录,全部运行时逻辑(管理域 API、CLI、进程管理原语)TS 化收进 backend 源码;单端口 29770 承载网关 + 管理台(管理 API 挂登录鉴权),29771 退役 |
31
32
 
32
33
  ## 3. 架构设计
33
34
 
@@ -35,18 +36,16 @@ ellm-proxy stop # 停止后台服务
35
36
 
36
37
  ```
37
38
  ellm-proxy/
38
- ├── bin/ellm-proxy.js # CLI 入口(npm bin 指向;纯 JS 零依赖)
39
- ├── dist/server.js # 发布版服务本体(esbuild 从 backend 源码直接构建 + bin shebang banner)
40
- ├── dist/web-ui/ # web-ui vite 构建产物(manager 管理服务托管于 127.0.0.1:<主端口+1>/)
41
- ├── manager/ # 管理服务(独立常驻):进程控制 + harness 开关 + web-ui 托管 + /api 反代
42
- ├── scripts/build-server.mjs # 发布构建(纯 esbuild,无任何源码替换)
43
- ├── scripts/build-package.mjs # 聚合 + 门禁(prepack:构建 server → 拷 web-ui → 复制 example → shebang 校验)
44
- └── release/ → 已删除 # 曾存在的"包模式路径模块"已被全 env 化方案取代
39
+ ├── bin/ellm-proxy.js # CLI shim(import dist/cli.js;npm bin 指向)
40
+ ├── dist/server.js # 服务本体(esbuild 从 server 源码构建 + shebang/createRequire banner)
41
+ ├── dist/cli.js # CLI(esbuild server/src/cli 构建,与 server 共享 proc/paths 原语)
42
+ ├── dist/web-ui/ # web-ui vite 构建产物(服务进程托管于 http://127.0.0.1:<port>/)
43
+ ├── scripts/build-server.mjs | build-cli.mjs # 发布构建(esbuild 双入口)
44
+ ├── scripts/build-package.mjs # 聚合 + 门禁(prepack:构建 server+cli → 拷 web-ui → 复制 example → shebang 校验)
45
+ └── manager/、utils/、bin/commands/已删除(2026-09-06 单进程化,逻辑全部迁入 server 包源码(backend 包同日改名 server))
45
46
  ```
46
47
 
47
- 发布命令(仓库根):`pnpm pack:proxy` = `build:web` `npm pack ./ellm-proxy`(prepack 自动构建聚合)。
48
-
49
- 发布 manifest 要点:`engines >=22.13.0`(node:sqlite 无 flag 最低版);`bin=bin/ellm-proxy.js`;`files` 白名单(bin/、dist/、example、README、LICENSE、docs);不加 `private`;`dependencies` 为 `cac@^6`(CLI 框架)、`pm2@^7`(进程守护,局部随包分发)与 `get-node@^15`(解析满足 engines 的 node 作为 pm2 interpreter);bundle 自包含;发布时用 `npm publish`(不改写 manifest)。
48
+ 发布 manifest 要点:`engines >=22.13.0`(node:sqlite 无 flag 最低版);`bin=bin/ellm-proxy.js`;`files` 白名单(bin/、dist/、example、README、LICENSE、docs);**`dependencies` 为空**(0.0.8 起全部逻辑 bundle 自包含,pm2/get-node 等依赖已删除);发布时用 `npm publish`(不改写 manifest)。
50
49
 
51
50
  ### 3.2 路径全 env 化(核心设计)
52
51
 
@@ -54,7 +53,7 @@ ellm-proxy/
54
53
 
55
54
  | 配置 | 环境变量 | 缺省公式(仓库模式自动正确) | 包模式来源 |
56
55
  | --- | --- | --- | --- |
57
- | 应用目录(配置/数据基准) | `ELLM_APP_DIR` | `backend/`(hereDir 推导) | start 脚本注入 = 工作目录(默认 `~/.ellm-proxy`) |
56
+ | 应用目录(配置/数据基准) | `ELLM_APP_DIR` | `server/`(hereDir 推导) | start 脚本注入 = 工作目录(默认 `~/.ellm-proxy`) |
58
57
  | 配置文件 | `ELLM_CONFIG` | `<APP_DIR>/ellm.config.json` | CLI `--config` 或用户 env |
59
58
  | 数据目录 | `ELLM_DATA_DIR` | `<APP_DIR>/data` | CLI `--data-dir` 或用户 env |
60
59
  | web-ui 静态资源 | `ELLM_WEBUI_DIR`(可选覆盖) | —(manager 按自身位置推导:包模式 `dist/web-ui` → 仓库模式 `web-ui/dist`) | manager 启动时自解析(CLI 不再注入) |
@@ -63,7 +62,7 @@ ellm-proxy/
63
62
 
64
63
  设计约束与边界:
65
64
 
66
- - **backend 源码零模式分支**:`paths.ts` 只有「env ?? 公式」两种取值,不存在模式判断;仓库开发模式不设任何 env 即工作。
65
+ - **server(原 backend)源码零模式分支**:`paths.ts` 只有「env ?? 公式」两种取值,不存在模式判断;仓库开发模式不设任何 env 即工作。
67
66
  - **包模式必须经 CLI 启动**:绕过它裸跑 `dist/server.js` 时 env 未注入,会因找不到配置退出(报错信息指引)。官方入口始终是 `ellm-proxy start/stop`。
68
67
  - **CLI 在转调前把路径参数统一转为绝对路径**:CLI 的 cwd 与 node 进程的工作目录不同,透传相对字符串会在另一基准下解析错位。
69
68
  - **CLI 负责首次配置自愈**(server 端只读不建):`ensureConfig()` 查找链与 server 端一致,创建后提示编辑或 web-ui 修改。
@@ -79,7 +78,7 @@ ellm-proxy/
79
78
 
80
79
  1. **`gateway/src/index.ts` 必须保持零顶层副作用**:backend 经 esbuild 全量内联 gateway 源码,任何 import 副作用都会执行。曾因 index.ts 顶层读 `ELLM_CONFIG` 装配 default app 与 backend 同名 env 撞车(实测踩坑)。独立网关装配已抽至 `src/standalone-app.ts`(`buildStandaloneApp()`)。
81
80
  2. **包内布局约束**:`dist/web-ui/` 与 `dist/server.js` 同级(manager 缺省推导依赖此布局;`ELLM_WEBUI_DIR` 可覆盖)。
82
- 3. **`backend/ellm.config.json` 仍是配置唯一数据源**(原样读写不变式在发布包同样成立)。
81
+ 3. **`server/ellm.config.json`(原 backend/)仍是配置唯一数据源**(原样读写不变式在发布包同样成立)。
83
82
 
84
83
  ## 4. 方案演进史(三轮,记录否决原因避免重走)
85
84
 
@@ -97,3 +96,33 @@ ellm-proxy/
97
96
  - CLI:`-h`、`config`(首次创建)、`start`(参数透传链 + env 注入 + 401/200 鉴权)、`stop`(树杀 + 端口释放)、`restart`(运行中/未运行两态)
98
97
  - 仓库产物模式(零 env)与包模式(CLI 注入)双模式启动均正常
99
98
  - 包目录零读写(`ls` 包目录无新增文件)、`npm pack --dry-run` 核对 tarball 无 `src/`/`test/`/真实配置
99
+
100
+ ## 6. 2026-09-06 重构:去 pm2 + 单进程单端口(决策 13)
101
+
102
+ ### 6.1 动机与决策过程(注:backend 包于 2026-09-06 改名 server,本节与 §4 历史 backend 均指该包)
103
+
104
+ - pm2 是发布包最重的依赖(安装慢、daemon 常驻内存、env 白名单清洗等实测坑),且其 daemon 行为黑盒不可控;
105
+ - manager「独立常驻以远程拉起主服务」的设计前提是主服务可能被 pm2 单独停止——去 pm2 后前提消失;
106
+ - 管理域代码(manager/api + harness + updater + 静态托管 ≈ 700 行)与 CLI(bin + utils ≈ 600 行)随单进程化全部 TS 化收进 `server/src/`(admin/、proc/、cli/;backend 包同日改名 server),发布壳只剩构建产物,JS/TS 边界消失。
107
+
108
+ 曾评估的替代方案(详见会话记录):更新者子进程方案(detached updater,被 D 方案吸收)、postinstall 自启(故障语义差,否决)、版本化安装 + current 切换(VS Code 式,终态最优但工程量大,留作后续演进)、OS 调度更新(粒度太粗)。更新链路定稿为 **D 方案(启动时生效)+ self-respawn**:看门狗/手动更新只写 pendingUpdate 标记并 respawn,新进程引导时装 `npm i -g` 后双跳换新代码——停机窗口与旧 pm2 流程相同(= npm 安装时长),实现量最小,回滚语义天然(装包失败磁盘仍是旧版)。
109
+
110
+ ### 6.2 终态架构
111
+
112
+ - **单进程单端口**:`dist/server.js` 同时承载网关 `/v1/*`、管理台 `/api/*`(登录鉴权)、管理域 API(`/status` `/processes` `/restart` `/autostart` `/update/*` `/harness/*`,原 manager 免鉴权裸路径 → 挂 `authMiddleware`)、web-ui 静态托管(注册顺序最后,gateway 子应用无 catch-all 不遮蔽)。29771 退役。
113
+ - **进程管理原语(server/src/proc/)**:`run.json` 运行记录(pid/端口/startedAt/serverJs);`spawnDetached`(日志重定向到 logs/);`selfRespawn`(孵化后继 + 退出,`ELLM_RESPAWNED=1` 时后继进程对 EADDRINUSE 重试绑定 300ms×10s 而非顺延端口);`killTree`(win taskkill /T /F,posix 杀进程组);`waitHttpOnline`(端口真实可服务才算就绪);`installCrashRecovery`(uncaughtException → respawn,计数器 1 分钟 10 次封顶;SIGKILL/断电无守护,由自启项兜底)。
114
+ - **CLI(server/src/cli/)**:start = 配置自愈 → 旧 pm2 清理 → 已运行检测 → 端口预算(顺延)→ spawn detached → run.json+HTTP 探活;stop = 旧 pm2 清理 → 按 run.json pid 杀树。旧 pm2 清理数据源是 `~/.pm2/pids/<name>-<pmId>.pid`(**dump.pm2 已加密不可解析**,实测踩坑);守护仅在存活 pm2 应用全部是 ellm-proxy 系时才杀(这台机器曾有其他 pm2 应用)。
115
+ - **解释器解析保留**(proc/interpreter.ts + get-node):服务依赖 node:sqlite,CLI 自身 node < 22.13 时解析/下载 node 22 再 spawn;自启项内容含 nodePath + ELLM_* env 快照,保证登录启动与手动启动同参。
116
+
117
+ ### 6.3 实测踩坑记录
118
+
119
+ - **pm2 pid 文件命名**:`pids/<name>-<pmId>.pid` 而非 `<pmId>.pid`,且 dump.pm2 主文件 require 出来是空对象(非文档所述纯数组)——旧版迁移清理必须以 pids 目录 + 文件名匹配为准。
120
+ - **esbuild ESM bundle 的 CJS 依赖**:get-node 链路的 tmp-promise、pidusage 内部 `require("util")` 会抛 "Dynamic require"——banner 加 `createRequire` 兜底(三个构建脚本统一)。
121
+ - **管理域 gatewayBase 必须每请求现算**:ctx.port 在 listen 后才回填,sub-app 创建时固化会得到 `:0`。
122
+ - **CLI 自身跑在旧 node 上**:dist/cli.js 的 bundle target node22 但语法兼容 18,真正跑服务前必须先解析解释器。
123
+
124
+ ### 6.4 已知取舍
125
+
126
+ - 更新期间(npm 安装时长)服务不可用——与旧 pm2 流程一致;停机窗口能缩短到几秒的版本化安装方案(B)留作演进。
127
+ - SIGKILL/断电无自动拉起(pm2 autorestart 的唯一真损失)——由开机自启项兜底。
128
+ - 管理域 API 从「仅本机免鉴权」升级为「登录鉴权」,外部直调脚本需带 token;29771 书签失效。