ezllm 0.0.3 → 0.0.5

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 (27) hide show
  1. package/bin/ellm-proxy.js +1 -1
  2. package/dist/cli.js +86 -27664
  3. package/dist/server.js +244 -42293
  4. package/dist/web-ui/assets/{DashboardView-fRDkGq0g.js → DashboardView-BE5e2n6u.js} +1 -1
  5. package/dist/web-ui/assets/{LayoutView-CK_MaWN7.js → LayoutView-Div_5EBT.js} +1 -1
  6. package/dist/web-ui/assets/{LoginView-B1naaT8E.js → LoginView-cCi4Iwct.js} +1 -1
  7. package/dist/web-ui/assets/{LogsView-YtH8gJPU.js → LogsView-CNop4N9G.js} +1 -1
  8. package/dist/web-ui/assets/ProvidersView-0jNbWJE5.css +1 -0
  9. package/dist/web-ui/assets/ProvidersView-CTqR-k3L.js +31 -0
  10. package/dist/web-ui/assets/ServiceStatusView-D1pXtHQJ.js +5 -0
  11. package/dist/web-ui/assets/ServiceStatusView-DN0AwyLo.css +1 -0
  12. package/dist/web-ui/assets/{SettingsView-DepOaZYg.js → SettingsView--ZDLNPOa.js} +1 -1
  13. package/dist/web-ui/assets/{SetupView-BwDwokAd.js → SetupView-BqrEqw3F.js} +1 -1
  14. package/dist/web-ui/assets/{config-CdOq1p3C.js → config-D_yFz2xb.js} +1 -1
  15. package/dist/web-ui/assets/{index-CgzCvxTY.js → index-B2BlgF9y.js} +2 -2
  16. package/dist/web-ui/assets/providers-ABdiyjVl.js +1 -0
  17. package/dist/web-ui/assets/{useLoadError-UA3r7sR9.js → useLoadError-Vo8GaucY.js} +1 -1
  18. package/dist/web-ui/assets/{useManagerApi-CBQNomtR.js → useManagerApi-CnyeOfgT.js} +1 -1
  19. package/dist/web-ui/assets/{useToast-DNMhOOeI.js → useToast-Cve-kXIC.js} +1 -1
  20. package/dist/web-ui/index.html +1 -1
  21. package/docs/DESIGN.md +8 -8
  22. package/package.json +1 -1
  23. package/dist/web-ui/assets/ProvidersView-Caqpg_0F.css +0 -1
  24. package/dist/web-ui/assets/ProvidersView-DWtS_e7t.js +0 -31
  25. package/dist/web-ui/assets/ServiceStatusView-D3ybS2T-.js +0 -39
  26. package/dist/web-ui/assets/ServiceStatusView-DrPoYMN-.css +0 -1
  27. package/dist/web-ui/assets/providers-i7t_wEzk.js +0 -1
@@ -0,0 +1,5 @@
1
+ import{d as re,r as i,l as ce,y as ie,e as c,i as W,c as E,w as u,j,h as L,A as x,a as f,o as r,g as y,f as l,b as ue,F as I,z as O,t as _,p as M,k as de,ad as ge,a3 as ve,_ as me}from"./index-B2BlgF9y.js";import{u as pe}from"./useToast-Cve-kXIC.js";import{u as fe}from"./useLoadError-Vo8GaucY.js";import{c as ye}from"./clipboard-aCBIMfw7.js";const _e={class:"service-status-page"},he={class:"flex-between"},we={class:"flex-gap-8"},$e={class:"flex-gap-8"},Se={class:"gantt-wrap"},ke={key:0,class:"gantt"},Le={class:"gantt-row gantt-head"},xe={class:"col-cells"},Me=["title"],be={class:"cell-head-date"},Te={class:"col-name"},Ce=["title","onClick"],De=["title"],Ne={class:"col-cells"},Re=["title"],Ae={key:1,class:"gantt-empty"},P=36e5,G=24,He="#909399",Ee=re({__name:"ServiceStatusView",setup(Ie){const{toast:b}=pe(),{loadError:V,run:q}=fe("延迟数据加载失败,请重试"),v=i([]),F=i(!0),T=i(!1),h=i(null),w=i(!1),C=i(!1);let g,m;const $=i(D()),S=i(new Map);function D(){const t=new Date,e=new Date(t.getFullYear(),t.getMonth(),t.getDate(),t.getHours()).getTime();return Array.from({length:G},(a,n)=>e-(G-1-n)*P)}const z=t=>{const e=new Date(t);if(Number.isNaN(e.getTime()))return"-";const a=n=>String(n).padStart(2,"0");return`${e.getFullYear()}-${a(e.getMonth()+1)}-${a(e.getDate())} ${a(e.getHours())}:00`},J=M(()=>$.value.map(t=>{const e=new Date(t),a=s=>String(s).padStart(2,"0"),n=a(e.getHours());return{start:t,dateLabel:`${a(e.getMonth()+1)}-${a(e.getDate())}`,hourLabel:n,fullLabel:`${z(t)} - ${n}:59`}})),k=t=>t==null?"-":t<1e3?`${String(t)}ms`:`${(t/1e3).toFixed(1)}s`,K=[103,194,58],B=[230,162,60],Q=[245,108,108],X=M(()=>{const t=[];for(const n of S.value.values())for(const s of n.values())s.avgLatencyMs!==null&&t.push(s.avgLatencyMs);if(t.length===0)return{min:0,max:0};let e=t[0],a=t[0];for(const n of t)n<e&&(e=n),n>a&&(a=n);return{min:e,max:a}}),Z=t=>{const{min:e,max:a}=X.value,n=a===e?0:(t-e)/(a-e),[s,A]=n<=.5?[K,B]:[B,Q],H=n<=.5?n*2:(n-.5)*2,p=s.map((o,d)=>Math.round(o+(A[d]-o)*H));return`rgba(${String(p[0])}, ${String(p[1])}, ${String(p[2])}, 0.9)`},N=(t,e)=>{const a=`${z(e)} - ${String(new Date(e+P).getHours()).padStart(2,"0")}:00`;return t===void 0||t.total===0?`${a}
2
+ 无探测数据`:`${a}
3
+ 平均延迟 ${k(t.avgLatencyMs)}
4
+ 成功 ${String(t.success)} / 失败 ${String(t.failed)}`},U=M(()=>te.value.map(t=>{const e=S.value.get(t.model),a=$.value.map(n=>{const s=e==null?void 0:e.get(n);return s!==void 0&&s.failed>0?{start:n,className:"cell--down",style:{backgroundColor:He},title:N(s,n)}:s===void 0||s.avgLatencyMs===null?{start:n,className:"cell--empty",style:{},title:N(s,n)}:{start:n,className:"",style:{backgroundColor:Z(s.avgLatencyMs)},title:N(s,n)}});return{model:t.model,avg24h:t.avg24h,statusTitle:[t.avg24h===null?"最近 24 小时无成功探测记录":`最近 24 小时成功探测平均延迟 ${k(t.avg24h)}`,t.lastLatencyMs==null?"最近一次探测:无成功延迟记录":`最近一次探测延迟 ${k(t.lastLatencyMs)}`].join(`
5
+ `),cells:a}})),ee=(t,e)=>t===null&&e===null?0:t===null?1:e===null?-1:t-e,te=M(()=>[...v.value].sort((t,e)=>{const a=t.lastOk===!1,n=e.lastOk===!1;if(a!==n)return a?1:-1;const s=ee(t.avg24h,e.avg24h);return s!==0?s:t.model.localeCompare(e.model)})),ae=t=>{if(t===null)return"-";const e=new Date(t);if(Number.isNaN(e.getTime()))return"-";const a=n=>String(n).padStart(2,"0");return`${e.getFullYear()}-${a(e.getMonth()+1)}-${a(e.getDate())} ${a(e.getHours())}:${a(e.getMinutes())}:${a(e.getSeconds())}`},Y=async t=>{if(t.length===0){S.value=new Map;return}const e=await Promise.all(t.map(async a=>{try{const n=await x.get(`/api/latency/trend?model=${encodeURIComponent(a)}&granularity=hour`);return[a,new Map((n.buckets??[]).map(s=>[s.bucketStart,s]))]}catch{return[a,new Map]}}));S.value=new Map(e)},R=()=>q(async()=>{try{const t=await x.get("/api/latency/history");v.value=t.models??[],T.value=t.running,h.value=t.lastRunAt,$.value=D(),await Y(v.value.map(e=>e.model))}finally{F.value=!1}}),ne=()=>R(),se=()=>{C.value?g=window.setInterval(()=>void R(),5e3):g!==void 0&&(window.clearInterval(g),g=void 0)},le=async()=>{w.value=!0;try{await x.post("/api/latency/run")}catch(t){b(t.message,"error"),w.value=!1;return}m=window.setInterval(async()=>{try{const t=await x.get("/api/latency/history");v.value=t.models??[],T.value=t.running,h.value=t.lastRunAt,t.running||(window.clearInterval(m),m=void 0,w.value=!1,$.value=D(),await Y(v.value.map(e=>e.model)))}catch{}},2e3)},oe=async t=>{await ye(t)?b("已复制到剪贴板","success"):b("复制失败","error")};return ce(()=>{R()}),ie(()=>{g!==void 0&&window.clearInterval(g),m!==void 0&&window.clearInterval(m)}),(t,e)=>{const a=f("el-button"),n=f("el-alert"),s=f("el-tag"),A=f("el-checkbox"),H=f("el-card"),p=de("loading");return r(),c("div",_e,[W(V)?(r(),E(n,{key:0,title:W(V),type:"error","show-icon":"",closable:!1,class:"mb-16"},{default:u(()=>[L(a,{size:"small",onClick:ne},{default:u(()=>[...e[1]||(e[1]=[y("重试",-1)])]),_:1})]),_:1},8,["title"])):j("",!0),L(H,{shadow:"never",class:"main-card"},{header:u(()=>[l("div",he,[l("div",we,[e[3]||(e[3]=l("span",{class:"fw-600"},"服务状态",-1)),T.value?(r(),E(s,{key:0,type:"warning",size:"small"},{default:u(()=>[...e[2]||(e[2]=[y("测试进行中",-1)])]),_:1})):h.value?(r(),E(s,{key:1,type:"info",size:"small"},{default:u(()=>[y(" 上次测试 "+_(ae(h.value)),1)]),_:1})):j("",!0)]),l("div",$e,[L(A,{modelValue:C.value,"onUpdate:modelValue":e[0]||(e[0]=o=>C.value=o),onChange:se},{default:u(()=>[...e[4]||(e[4]=[y(" 自动刷新(5s) ",-1)])]),_:1},8,["modelValue"]),L(a,{loading:w.value,onClick:le},{default:u(()=>[...e[5]||(e[5]=[y(" 延迟测试 ",-1)])]),_:1},8,["loading"])])])]),default:u(()=>[e[9]||(e[9]=l("div",{class:"legend"},[l("span",{class:"legend-text"},"延迟"),l("span",{class:"legend-text"},"低"),l("span",{class:"legend-gradient"}),l("span",{class:"legend-text"},"高"),l("span",{class:"legend-swatch legend-swatch--down"}),l("span",{class:"legend-text"},"该小时有失败探测"),l("span",{class:"legend-swatch legend-swatch--empty"}),l("span",{class:"legend-text"},"无探测数据")],-1)),ue((r(),c("div",Se,[e[8]||(e[8]=l("div",{class:"muted-sm hint"}," 右侧 24 格为最近 24 小时(每格 1 小时,本地整点对齐,最右格为当前小时)。 格子底色为当小时成功探测的平均延迟,全表统一色标(本期最快→最慢 绿→红); 当小时存在失败探测即视为不可用,显示灰色。 行按 24h 平均延迟升序,最近一次探测失败的模型沉底。 ",-1)),U.value.length>0?(r(),c("div",ke,[l("div",Le,[e[6]||(e[6]=l("div",{class:"col-name"},"模型名称",-1)),e[7]||(e[7]=l("div",{class:"col-status"},"状态",-1)),l("div",xe,[(r(!0),c(I,null,O(J.value,o=>(r(),c("div",{key:o.start,class:"cell cell--head",title:o.fullLabel},[l("span",be,_(o.dateLabel),1),l("span",null,_(o.hourLabel),1)],8,Me))),128))])]),(r(!0),c(I,null,O(U.value,o=>(r(),c("div",{key:o.model,class:"gantt-row"},[l("div",Te,[l("span",{class:"copy-text model-name",title:o.model,onClick:d=>oe(o.model)},_(o.model),9,Ce)]),l("div",{class:"col-status",title:o.statusTitle},_(k(o.avg24h)),9,De),l("div",Ne,[(r(!0),c(I,null,O(o.cells,d=>(r(),c("div",{key:d.start,class:ve(["cell",d.className]),style:ge(d.style),title:d.title},null,14,Re))),128))])]))),128))])):(r(),c("div",Ae,"暂无延迟数据,点击「延迟测试」开始"))])),[[p,F.value]])]),_:1})])}}}),Be=me(Ee,[["__scopeId","data-v-eb7e57c9"]]);export{Be as default};
@@ -0,0 +1 @@
1
+ @charset "UTF-8";.service-status-page[data-v-eb7e57c9]{height:calc(100vh - 124px);display:flex;flex-direction:column;overflow:hidden}.main-card[data-v-eb7e57c9]{flex:1;min-height:0;overflow:hidden;display:flex;flex-direction:column}.main-card[data-v-eb7e57c9] .el-card__body{height:100%;display:flex;flex-direction:column;overflow:hidden}.legend[data-v-eb7e57c9]{display:flex;align-items:center;gap:6px;flex-shrink:0;margin-bottom:8px;font-size:12px;color:#606266}.legend-gradient[data-v-eb7e57c9]{width:96px;height:10px;border-radius:2px;background:linear-gradient(90deg,#67c23a,#e6a23c,#f56c6c)}.legend-swatch[data-v-eb7e57c9]{width:14px;height:10px;border-radius:2px;margin-left:10px}.legend-swatch--down[data-v-eb7e57c9]{background-color:#909399}.legend-swatch--empty[data-v-eb7e57c9]{background-color:#f2f3f5;border:1px solid #ebeef5}.hint[data-v-eb7e57c9]{margin-bottom:12px;flex-shrink:0}.gantt-wrap[data-v-eb7e57c9]{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.gantt[data-v-eb7e57c9]{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden}.gantt-row[data-v-eb7e57c9]{display:flex;align-items:center;height:32px;border-bottom:1px solid #f2f6fc}.gantt-row[data-v-eb7e57c9]:hover{background-color:#fafcff}.gantt-head[data-v-eb7e57c9]{position:sticky;top:0;z-index:1;height:auto;min-height:34px;padding:2px 0;background-color:#fff;border-bottom:1px solid #ebeef5;font-size:12px;color:#909399;font-weight:600}.col-name[data-v-eb7e57c9]{flex:0 0 240px;width:240px;padding-right:12px;font-size:13px}.col-status[data-v-eb7e57c9]{flex:0 0 100px;width:100px;padding-right:12px;font-size:13px;color:#606266}.col-cells[data-v-eb7e57c9]{flex:1;min-width:0;display:flex}.cell[data-v-eb7e57c9]{flex:1 1 0;min-width:0;height:20px;box-shadow:inset -1px 0 #fff}.cell[data-v-eb7e57c9]:last-child{box-shadow:none}.cell--head[data-v-eb7e57c9]{height:auto;display:flex;flex-direction:column;align-items:center;line-height:1.15;font-weight:400;overflow:hidden;white-space:nowrap;font-size:10px}.cell-head-date[data-v-eb7e57c9]{color:#c0c4cc;font-size:9px}.cell--empty[data-v-eb7e57c9]{background-color:#f2f3f5}.gantt-empty[data-v-eb7e57c9]{flex:1;display:flex;align-items:center;justify-content:center;color:#909399;font-size:13px}.copy-text[data-v-eb7e57c9]{color:#409eff;cursor:pointer}.copy-text[data-v-eb7e57c9]:hover{text-decoration:underline}.model-name[data-v-eb7e57c9]{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@@ -1 +1 @@
1
- import{u as Z}from"./useToast-DNMhOOeI.js";import{u as re}from"./useLoadError-UA3r7sR9.js";import{d as ee,l as le,b as te,o as d,c as A,w as n,e as b,f as s,t as V,j as T,g as i,h as o,F as I,r as v,E as q,a as c,k as se,_ as ae,y as de,p as j,A as ve,i as Q,z as ce}from"./index-CgzCvxTY.js";import{u as pe}from"./config-CdOq1p3C.js";import{c as me}from"./clipboard-aCBIMfw7.js";import{m as L,w as fe}from"./useManagerApi-CBQNomtR.js";import{u as _e,i as ye}from"./version-BKHjE_LF.js";const we={class:"flex-between"},ge={class:"muted-sm mb-8"},be={class:"switch-row"},ke={class:"switch-info"},Ce={class:"switch-title"},$e={class:"switch-row"},Ve={class:"switch-info"},xe={class:"switch-title"},he={class:"switch-row"},Te={class:"switch-info"},Ae={class:"switch-title"},Se=ee({__name:"HarnessSwitches",setup(ne){const{toast:y}=Z(),x=v(!0),z=v(!1),p=v(null),S=v(null),u=v(null),P=v(null),k=v("success"),h=async g=>{g||(p.value?z.value=!0:x.value=!0);try{p.value=await L.harness(),S.value=null}catch(a){g||(S.value=a.message)}finally{g||(x.value=!1,z.value=!1)}};le(()=>h(!1));const B={claudeCode:{label:"Claude Code",file:"~/.claude/settings.json",toggle:L.setClaudeCode},codex:{label:"Codex CLI",file:"~/.codex/config.toml",toggle:L.setCodex},dsh:{label:"DeepSeek Harness",file:"~/.dsh/settings.yaml",toggle:L.setDsh}},C=async(g,a)=>{if(!p.value||u.value)return;const $=B[g],N=a?`确定开启 ${$.label} 代理?将改写 ${$.file}(开启前自动备份,关闭时从备份还原)。`:`确定关闭 ${$.label} 代理?将从备份还原 ${$.file}(无备份时移除代理相关配置)。`;try{await q.confirm(N,a?"开启确认":"关闭确认",{confirmButtonText:a?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}u.value=g;try{const f=await $.toggle(a);P.value=f.note??null,k.value=a?"success":"info",y(a?"已开启代理":"已关闭代理","success")}catch(f){y(f.message||"操作失败","error")}finally{u.value=null,await h(!0)}};return(g,a)=>{const $=c("el-button"),N=c("el-alert"),f=c("el-tag"),E=c("el-switch"),M=c("el-divider"),K=c("el-empty"),F=c("el-card"),R=se("loading");return te((d(),A(F,{shadow:"never"},{header:n(()=>[s("div",we,[a[7]||(a[7]=s("span",{class:"fw-600"},"代理开关",-1)),o($,{loading:z.value,onClick:a[0]||(a[0]=U=>h(!1))},{default:n(()=>[...a[6]||(a[6]=[i("刷新",-1)])]),_:1},8,["loading"])])]),default:n(()=>[p.value?(d(),b(I,{key:0},[s("div",ge," 将本机 CLI 工具的流量一键指向本机网关("+V(p.value.gatewayBase)+")。 开启前自动备份原配置为 *.ellm-bak(保留最早的原始备份),关闭时从备份还原。 ",1),P.value?(d(),A(N,{key:0,title:P.value,type:k.value,"show-icon":"",class:"mb-8",onClose:a[1]||(a[1]=U=>P.value=null)},null,8,["title","type"])):T("",!0),s("div",be,[s("div",ke,[s("div",Ce,[a[8]||(a[8]=i(" Claude Code ",-1)),o(f,{type:p.value.claudeCode?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(p.value.claudeCode?"已开启":"未开启"),1)]),_:1},8,["type"])]),a[9]||(a[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))]),o(E,{"model-value":p.value.claudeCode,loading:u.value==="claudeCode",onChange:a[2]||(a[2]=U=>C("claudeCode",!!U))},null,8,["model-value","loading"])]),o(M,{class:"switch-divider"}),s("div",$e,[s("div",Ve,[s("div",xe,[a[10]||(a[10]=i(" Codex CLI ",-1)),o(f,{type:p.value.codex?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(p.value.codex?"已开启":"未开启"),1)]),_:1},8,["type"])]),a[11]||(a[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))]),o(E,{"model-value":p.value.codex,loading:u.value==="codex",onChange:a[3]||(a[3]=U=>C("codex",!!U))},null,8,["model-value","loading"])]),o(M,{class:"switch-divider"}),s("div",he,[s("div",Te,[s("div",Ae,[a[12]||(a[12]=i(" DeepSeek Harness ",-1)),o(f,{type:p.value.dsh?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(p.value.dsh?"已开启":"未开启"),1)]),_:1},8,["type"])]),a[13]||(a[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))]),o(E,{"model-value":p.value.dsh,loading:u.value==="dsh",onChange:a[4]||(a[4]=U=>C("dsh",!!U))},null,8,["model-value","loading"])])],64)):(d(),A(K,{key:1,description:S.value||"暂无开关状态"},{default:n(()=>[o($,{type:"primary",onClick:a[5]||(a[5]=U=>h(!1))},{default:n(()=>[...a[14]||(a[14]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1})),[[R,x.value]])}}}),Ee=ae(Se,[["__scopeId","data-v-5084d579"]]),Ue={class:"service-settings"},ze={class:"flex-between"},Be={key:0,class:"switch-row"},Le={class:"switch-info"},Ie={class:"switch-title"},Pe={key:0,class:"switch-row"},He={class:"switch-info"},Ke={class:"switch-title"},De={class:"switch-desc"},Ne={key:0,class:"version-block"},Oe={class:"version-row"},je={class:"muted-sm"},Me={key:0,class:"muted-sm"},Re={key:1,class:"muted-sm"},Fe={class:"muted-sm"},Ye={class:"version-actions"},We=ee({__name:"ServiceUpdateSettings",setup(ne){const{toast:y}=Z(),x=v(!0),z=v(!1),p=v(null),S=v(null),u=v(null),P=v(null),k=v(null),h=v(!1),B=v(!1),C=v(null);let g=null;de(()=>g==null?void 0:g.abort());const a=async l=>{var e,r;if(!l){if(z.value)return;z.value=!0}try{const[_,m]=await Promise.allSettled([L.autostart(),L.update()]);p.value=_.status==="fulfilled"?_.value:null,S.value=_.status==="rejected"?(e=_.reason)==null?void 0:e.message:null,u.value=m.status==="fulfilled"?m.value:null,P.value=m.status==="rejected"?(r=m.reason)==null?void 0:r.message:null}finally{x.value=!1,z.value=!1}};le(()=>a(!1));const $=j(()=>{var l,e;return((e=(l=u.value)==null?void 0:l.lastCheck)==null?void 0:e.currentVersion)??null}),N=j(()=>ye($.value)),f=j(()=>{var l,e;return((e=(l=u.value)==null?void 0:l.lastCheck)==null?void 0:e.latestVersion)??null}),E=j(()=>{var l,e;return((e=(l=u.value)==null?void 0:l.lastCheck)==null?void 0:e.hasUpdate)===!0}),M=j(()=>{var e,r;const l=(r=(e=u.value)==null?void 0:e.lastCheck)==null?void 0:r.checkedAt;return l?new Date(l).toLocaleString():null}),K=j(()=>{var r;const l=(r=u.value)==null?void 0:r.lastApply;if(!l)return null;const e=l.appliedAt?new Date(l.appliedAt).toLocaleString():"";return`${l.fromVersion} → ${l.toVersion}${e?`(${e})`:""}`}),F=l=>l?new Date(l).toLocaleString():"",R=async l=>{if(!p.value||k.value)return;const e=l?"确定开启开机自启?将注册登录触发的自启项(登录时自动恢复服务)。":"确定关闭开机自启?将移除自启项,且此后首次启动不再自动注册。";try{await q.confirm(e,l?"开启确认":"关闭确认",{confirmButtonText:l?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}k.value="autostart";try{const r=await L.setAutostart(l);r.note?y(r.note,"success"):y(l?"开机自启已开启":"开机自启已关闭","success")}catch(r){y(r.message||"操作失败","error")}finally{k.value=null,await a(!0)}},U=async l=>{if(!u.value||k.value)return;const e=l?"确定开启自动更新?将每小时检查 npm 源,发现新版本自动安装并重启服务。":"确定关闭自动更新?不再自动检查(手动检查更新不受影响)。";try{await q.confirm(e,l?"开启确认":"关闭确认",{confirmButtonText:l?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}k.value="updateEnabled";try{const r=await L.setUpdateEnabled(l);r.note&&y(r.note,"success")}catch(r){y(r.message||"操作失败","error")}finally{k.value=null,await a(!0)}},D=async()=>{h.value=!0;try{const l=await L.checkUpdate();C.value=l.hasUpdate?`发现新版本 ${l.latestVersion}(当前 ${l.currentVersion}):点击「立即更新」即可安装(服务自动重启,期间管理台短暂不可访问)`:`已是最新版本 ${l.currentVersion}(检查于 ${F(l.checkedAt)})`,await a(!0)}catch(l){y(l.message||"检查更新失败","error")}finally{h.value=!1}},t=async()=>{var e;try{await q.confirm(`确定更新到 ${f.value}?服务会自动重启并在启动时安装新版本:重启期间管理台短暂不可访问(预计 1~3 分钟),更新完成后自动恢复。`,"更新确认",{confirmButtonText:"立即更新",cancelButtonText:"取消",type:"warning"})}catch{return}B.value=!0;const l=new AbortController;g=l;try{const r=await L.update(),_=await L.applyUpdate();if(!_.updated){C.value=`已是最新版本 ${_.toVersion}`;return}C.value=`正在重启服务并安装 ${_.toVersion}(预计 1~3 分钟):重启期间管理台短暂不可访问,完成后这里会自动显示结果。`;const m=await fe({targetVersion:_.toVersion,sinceAppliedAt:((e=r.lastApply)==null?void 0:e.appliedAt)??null,signal:l.signal});if(l.signal.aborted)return;m&&!m.error?(y(`已更新到 ${m.toVersion}`,"success"),C.value=`已更新 ${m.fromVersion} → ${m.toVersion},服务已在新版本上运行。`):m!=null&&m.error?(y(`更新失败:${m.error}`,"error"),C.value=_e(m.error)):C.value="更新仍在进行(安装耗时较长):稍后刷新页面查看最新状态。"}catch(r){y(r.message||"更新失败","error")}finally{B.value=!1,g===l&&(g=null),l.signal.aborted||await a(!0)}};return(l,e)=>{const r=c("el-button"),_=c("el-tag"),m=c("el-switch"),Y=c("el-empty"),O=c("el-card"),W=c("el-link"),X=c("el-alert"),G=se("loading");return te((d(),b("div",Ue,[o(O,{shadow:"never",class:"mb-16"},{header:n(()=>[s("div",ze,[e[7]||(e[7]=s("span",{class:"fw-600"},"开机自启",-1)),o(r,{loading:z.value,onClick:e[0]||(e[0]=H=>a(!1))},{default:n(()=>[...e[6]||(e[6]=[i("刷新",-1)])]),_:1},8,["loading"])])]),default:n(()=>[p.value?(d(),b("div",Be,[s("div",Le,[s("div",Ie,[e[8]||(e[8]=i(" 登录时自动启动服务 ",-1)),o(_,{type:p.value.enabled?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(p.value.enabled?"已开启":"未开启"),1)]),_:1},8,["type"])]),e[9]||(e[9]=s("div",{class:"switch-desc"},[i(" 登录操作系统时自动启动服务(无需管理员权限)。 关闭后首次启动 "),s("code",null,"ezllm"),i(" 也不会再自动注册,重新开启即可解除。 ")],-1))]),o(m,{"model-value":p.value.enabled,loading:k.value==="autostart",onChange:e[1]||(e[1]=H=>R(!!H))},null,8,["model-value","loading"])])):(d(),A(Y,{key:1,description:S.value||"暂无自启状态"},{default:n(()=>[o(r,{type:"primary",onClick:e[2]||(e[2]=H=>a(!1))},{default:n(()=>[...e[10]||(e[10]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1}),o(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(()=>[u.value?(d(),b("div",Pe,[s("div",He,[s("div",Ke,[e[12]||(e[12]=i(" 自动检查并安装新版本 ",-1)),o(_,{type:u.value.enabled?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(u.value.enabled?"已开启":"未开启"),1)]),_:1},8,["type"])]),s("div",De,[u.value.supported?(d(),b(I,{key:1},[i(" 每小时检查 npm 源,发现新版本自动重启服务并在启动时安装(更新期间服务短暂中断); 关闭后仍可在下方手动检查与更新。 ")],64)):(d(),b(I,{key:0},[i(" 当前为源码运行模式,更新功能不可用(仅 npm 全局安装的包支持自动更新)。 ")],64))])]),u.value.supported?(d(),A(m,{key:0,"model-value":u.value.enabled,loading:k.value==="updateEnabled",onChange:e[3]||(e[3]=H=>U(!!H))},null,8,["model-value","loading"])):T("",!0)])):(d(),A(Y,{key:1,description:P.value||"暂无更新状态"},{default:n(()=>[o(r,{type:"primary",onClick:e[4]||(e[4]=H=>a(!1))},{default:n(()=>[...e[13]||(e[13]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1}),o(O,{shadow:"never"},{header:n(()=>[...e[14]||(e[14]=[s("div",{class:"flex-between"},[s("span",{class:"fw-600"},"版本信息")],-1)])]),default:n(()=>[u.value?(d(),b("div",Ne,[s("div",Oe,[e[18]||(e[18]=s("span",{class:"version-label"},"当前版本",-1)),o(_,{size:"small"},{default:n(()=>[i(V($.value||"未知"),1)]),_:1}),N.value?(d(),A(_,{key:0,type:"warning",size:"small","disable-transitions":""},{default:n(()=>[...e[15]||(e[15]=[i("本地构建",-1)])]),_:1})):T("",!0),f.value?(d(),b(I,{key:1},[e[17]||(e[17]=s("span",{class:"version-label"},"最新版本",-1)),o(_,{type:E.value?"warning":"success",size:"small","disable-transitions":""},{default:n(()=>[i(V(f.value),1)]),_:1},8,["type"]),E.value?(d(),A(_,{key:0,type:"warning",size:"small","disable-transitions":""},{default:n(()=>[...e[16]||(e[16]=[i("发现新版本",-1)])]),_:1})):T("",!0)],64)):T("",!0)]),s("div",je,[i(" 上次检查:"+V(M.value||"尚未检查")+" ",1),u.value.updating?(d(),b(I,{key:0},[i(" · 更新执行中...")],64)):T("",!0)]),u.value.supported?T("",!0):(d(),b("div",Me," 源码运行模式:手动检查与更新不可用。 ")),K.value?(d(),b("div",Re," 上次更新:"+V(K.value),1)):T("",!0),s("div",Fe,[e[20]||(e[20]=i(" npm 包主页:",-1)),o(W,{type:"primary",href:"https://www.npmjs.com/package/ezllm",target:"_blank"},{default:n(()=>[...e[19]||(e[19]=[i("npmjs.com/package/ezllm",-1)])]),_:1})]),s("div",Ye,[o(r,{loading:h.value,disabled:!u.value.supported||u.value.updating,onClick:D},{default:n(()=>[i(V(h.value?"检查中...":"检查更新"),1)]),_:1},8,["loading","disabled"]),E.value&&u.value.supported?(d(),A(r,{key:0,type:"primary",loading:B.value,disabled:u.value.updating,onClick:t},{default:n(()=>[i(V(B.value?"更新中...":`立即更新到 ${f.value}`),1)]),_:1},8,["loading","disabled"])):T("",!0)]),C.value?(d(),A(X,{key:2,title:C.value,type:"info","show-icon":"",class:"mt-8",onClose:e[5]||(e[5]=H=>C.value=null)},null,8,["title"])):T("",!0)])):(d(),A(Y,{key:1,description:"更新状态不可用"}))]),_:1})])),[[G,x.value]])}}}),Xe=ae(We,[["__scopeId","data-v-d51cfdf5"]]),qe={class:"flex-col-gap"},Ge={class:"flex-between"},Je={class:"muted-sm"},Qe={key:0},Ze={class:"login-row"},el={class:"muted-sm"},ll={class:"flex-between"},tl={class:"monitor-row"},sl={class:"save-bar"},al=ee({__name:"SettingsView",setup(ne){const{toast:y}=Z(),x=pe(),z=v("basic"),p=v(!0),S=v(!1),u=v(""),P=async()=>{if(!u.value)return;await me(u.value)?y("已复制到剪贴板","success"):y("复制失败","error")},k=v(29770),h=v(null),B=v(!0),C=v(null),g=v(null),a=v(""),$=v(""),N=j(()=>u.value.trim()!==""||a.value!==""&&$.value!==""),f=v(!1),E=v(60),M=[{label:"10分钟",value:10},{label:"30分钟",value:30},{label:"1小时",value:60},{label:"2小时",value:120},{label:"5小时",value:300},{label:"8小时",value:480},{label:"12小时",value:720},{label:"24小时",value:1440}],{loadError:K,run:F}=re("设置加载失败,请重试"),R=()=>F(async()=>{var D;try{await x.load(),u.value=x.config.masterKey||"",k.value=x.config.port??29770;const t=(D=x.config["web-ui"])==null?void 0:D["login-account"];a.value=(t==null?void 0:t.username)||"",$.value=(t==null?void 0:t.password)||"",B.value=x.config.allowLan!==!1;const l=x.config.latencyTest;f.value=(l==null?void 0:l.enabled)===!0,E.value=(l==null?void 0:l.intervalMinutes)??60;try{const e=await ve.get("/api/status");h.value=e.listenPort,C.value=e.listenHost??null,g.value=e.listenSource??null}catch{h.value=null,C.value=null,g.value=null}}finally{p.value=!1}}),U=async()=>{var t;S.value=!0;const D=f.value&&((t=x.config.latencyTest)==null?void 0:t.enabled)!==!0;try{const l=await x.update(e=>{var r;e.masterKey=u.value.trim(),e.port=k.value,e.allowLan=B.value,e.latencyTest={enabled:f.value,intervalMinutes:E.value},e["web-ui"]??(e["web-ui"]={}),(r=e["web-ui"])["login-account"]??(r["login-account"]={}),e["web-ui"]["login-account"].username=a.value.trim(),e["web-ui"]["login-account"].password=$.value});y("设置已保存并生效","success"),D&&y("已开启自动测试:服务将定期直连所有上游发送探测请求,有少量 token 消耗","warning"),l.restartHint&&y(l.restartHint,"warning")}catch(l){y(l.message,"error")}finally{S.value=!1}};return le(R),(D,t)=>{const l=c("el-button"),e=c("el-alert"),r=c("el-tag"),_=c("el-input"),m=c("el-form-item"),Y=c("el-input-number"),O=c("el-form"),W=c("el-card"),X=c("el-switch"),G=c("el-divider"),H=c("el-option"),oe=c("el-select"),J=c("el-tab-pane"),ie=c("el-tabs"),ue=se("loading");return d(),A(ie,{modelValue:z.value,"onUpdate:modelValue":t[7]||(t[7]=w=>z.value=w),class:"settings-tabs"},{default:n(()=>[o(J,{label:"基本设置",name:"basic"},{default:n(()=>[te((d(),b("div",qe,[Q(K)?(d(),A(e,{key:0,title:Q(K),type:"error","show-icon":"",closable:!1},{default:n(()=>[o(l,{size:"small",onClick:R},{default:n(()=>[...t[8]||(t[8]=[i("重试",-1)])]),_:1})]),_:1},8,["title"])):T("",!0),o(W,{shadow:"never"},{header:n(()=>[s("div",Ge,[t[10]||(t[10]=s("span",{class:"fw-600"},"网关访问",-1)),o(r,{type:"success"},{default:n(()=>[...t[9]||(t[9]=[i("运行中",-1)])]),_:1})])]),default:n(()=>[o(O,{"label-position":"top"},{default:n(()=>[o(m,{label:"API 密钥 (masterKey)"},{default:n(()=>[o(_,{modelValue:u.value,"onUpdate:modelValue":t[0]||(t[0]=w=>u.value=w),placeholder:"sk-1234",clearable:""},{append:n(()=>[o(l,{onClick:P},{default:n(()=>[...t[11]||(t[11]=[i("复制",-1)])]),_:1})]),_:1},8,["modelValue"]),t[12]||(t[12]=s("div",{class:"muted-sm"}," 客户端请求 /v1/* 时的认证密钥(Bearer <masterKey>;留空 = 本地放行) ",-1))]),_:1}),o(m,{label:"监听端口 (port)"},{default:n(()=>[o(Y,{modelValue:k.value,"onUpdate:modelValue":t[1]||(t[1]=w=>k.value=w),min:1,max:65535,step:1,style:{width:"220px"}},null,8,["modelValue"]),s("div",Je,[t[14]||(t[14]=i(" 网关与管理台 API 的监听端口,保存后重启生效 ",-1)),h.value!==null?(d(),b(I,{key:0},[t[13]||(t[13]=i(" ;当前实际监听 ",-1)),s("b",null,V(h.value),1),h.value!==k.value?(d(),b("span",Qe,"(由 PORT 环境变量覆盖,此配置值不生效)")):T("",!0)],64)):T("",!0)])]),_:1})]),_:1})]),_:1}),o(W,{shadow:"never"},{header:n(()=>[...t[15]||(t[15]=[s("span",{class:"fw-600"},"安全与登录",-1)])]),default:n(()=>[o(O,{"label-position":"top"},{default:n(()=>[o(m,{label:"管理台登录账号"},{default:n(()=>[s("div",Ze,[o(_,{modelValue:a.value,"onUpdate:modelValue":t[2]||(t[2]=w=>a.value=w),placeholder:"用户名",style:{width:"200px"}},null,8,["modelValue"]),o(_,{modelValue:$.value,"onUpdate:modelValue":t[3]||(t[3]=w=>$.value=w),type:"password",placeholder:"密码(至少 6 位)","show-password":"",style:{width:"200px"}},null,8,["modelValue"])]),t[16]||(t[16]=s("div",{class:"muted-sm"}," 管理台登录凭证;留空 = 免登录直接访问(仅本机使用时推荐)。 修改后立即生效,需用新账号重新登录 ",-1))]),_:1}),o(m,{label:"允许局域网访问 (allowLan)"},{default:n(()=>[o(X,{modelValue:B.value,"onUpdate:modelValue":t[4]||(t[4]=w=>B.value=w)},null,8,["modelValue"]),s("div",el,[t[19]||(t[19]=i(" 开启 = 绑定全部网卡,局域网内设备可访问;关闭 = 仅本机 127.0.0.1。保存后需重启生效 ",-1)),g.value==="env"?(d(),b(I,{key:0},[t[17]||(t[17]=i(" ;当前实际监听 ",-1)),s("b",null,V(C.value),1),t[18]||(t[18]=i("(由 HOST 环境变量覆盖,此开关不生效) ",-1))],64)):N.value?T("",!0):(d(),b(I,{key:1},[i(" ;未设置任何凭据(密钥与登录账号皆无)时始终仅绑本机 ")],64))])]),_:1})]),_:1})]),_:1}),o(W,{shadow:"never"},{header:n(()=>[s("div",ll,[t[20]||(t[20]=s("span",{class:"fw-600"},"模型服务监控",-1)),o(r,{type:f.value?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(f.value?"已开启":"未开启"),1)]),_:1},8,["type"])])]),default:n(()=>[s("div",tl,[t[21]||(t[21]=s("div",{class:"monitor-info"},[s("div",{class:"monitor-title"},"自动测试模型服务状态"),s("div",{class:"muted-sm"}," 按设定间隔自动直连所有上游模型发送极小探测请求(max_tokens: 1,有少量 token 消耗); 测试结果展示在「服务状态」页面 ")],-1)),o(X,{modelValue:f.value,"onUpdate:modelValue":t[5]||(t[5]=w=>f.value=w)},null,8,["modelValue"])]),f.value?(d(),b(I,{key:0},[o(G,{style:{margin:"12px 0"}}),o(O,{"label-position":"top"},{default:n(()=>[o(m,{label:"测试间隔",style:{"margin-bottom":"0"}},{default:n(()=>[o(oe,{modelValue:E.value,"onUpdate:modelValue":t[6]||(t[6]=w=>E.value=w),style:{width:"220px"}},{default:n(()=>[(d(),b(I,null,ce(M,w=>o(H,{key:w.value,label:w.label,value:w.value},null,8,["label","value"])),64))]),_:1},8,["modelValue"]),t[22]||(t[22]=s("div",{class:"muted-sm"}," 自动测试的执行间隔,保存后即时生效(无需重启) ",-1))]),_:1})]),_:1})],64)):T("",!0)]),_:1}),s("div",sl,[o(l,{type:"primary",size:"large",loading:S.value,disabled:S.value||Q(K)!==null,onClick:U},{default:n(()=>[i(V(S.value?"保存中...":"保存设置"),1)]),_:1},8,["loading","disabled"])])])),[[ue,p.value]])]),_:1}),o(J,{label:"代理开关",name:"harness",lazy:""},{default:n(()=>[o(Ee)]),_:1}),o(J,{label:"服务与更新",name:"service",lazy:""},{default:n(()=>[o(Xe)]),_:1})]),_:1},8,["modelValue"])}}}),cl=ae(al,[["__scopeId","data-v-4c888be1"]]);export{cl as default};
1
+ import{u as Z}from"./useToast-Cve-kXIC.js";import{u as re}from"./useLoadError-Vo8GaucY.js";import{d as ee,l as le,b as te,o as d,c as A,w as n,e as b,f as s,t as V,j as T,g as i,h as o,F as I,r as v,E as q,a as c,k as se,_ as ae,y as de,p as j,A as ve,i as Q,z as ce}from"./index-B2BlgF9y.js";import{u as pe}from"./config-D_yFz2xb.js";import{c as me}from"./clipboard-aCBIMfw7.js";import{m as L,w as fe}from"./useManagerApi-CnyeOfgT.js";import{u as _e,i as ye}from"./version-BKHjE_LF.js";const we={class:"flex-between"},ge={class:"muted-sm mb-8"},be={class:"switch-row"},ke={class:"switch-info"},Ce={class:"switch-title"},$e={class:"switch-row"},Ve={class:"switch-info"},xe={class:"switch-title"},he={class:"switch-row"},Te={class:"switch-info"},Ae={class:"switch-title"},Se=ee({__name:"HarnessSwitches",setup(ne){const{toast:y}=Z(),x=v(!0),z=v(!1),p=v(null),S=v(null),u=v(null),P=v(null),k=v("success"),h=async g=>{g||(p.value?z.value=!0:x.value=!0);try{p.value=await L.harness(),S.value=null}catch(a){g||(S.value=a.message)}finally{g||(x.value=!1,z.value=!1)}};le(()=>h(!1));const B={claudeCode:{label:"Claude Code",file:"~/.claude/settings.json",toggle:L.setClaudeCode},codex:{label:"Codex CLI",file:"~/.codex/config.toml",toggle:L.setCodex},dsh:{label:"DeepSeek Harness",file:"~/.dsh/settings.yaml",toggle:L.setDsh}},C=async(g,a)=>{if(!p.value||u.value)return;const $=B[g],N=a?`确定开启 ${$.label} 代理?将改写 ${$.file}(开启前自动备份,关闭时从备份还原)。`:`确定关闭 ${$.label} 代理?将从备份还原 ${$.file}(无备份时移除代理相关配置)。`;try{await q.confirm(N,a?"开启确认":"关闭确认",{confirmButtonText:a?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}u.value=g;try{const f=await $.toggle(a);P.value=f.note??null,k.value=a?"success":"info",y(a?"已开启代理":"已关闭代理","success")}catch(f){y(f.message||"操作失败","error")}finally{u.value=null,await h(!0)}};return(g,a)=>{const $=c("el-button"),N=c("el-alert"),f=c("el-tag"),E=c("el-switch"),M=c("el-divider"),K=c("el-empty"),F=c("el-card"),R=se("loading");return te((d(),A(F,{shadow:"never"},{header:n(()=>[s("div",we,[a[7]||(a[7]=s("span",{class:"fw-600"},"代理开关",-1)),o($,{loading:z.value,onClick:a[0]||(a[0]=U=>h(!1))},{default:n(()=>[...a[6]||(a[6]=[i("刷新",-1)])]),_:1},8,["loading"])])]),default:n(()=>[p.value?(d(),b(I,{key:0},[s("div",ge," 将本机 CLI 工具的流量一键指向本机网关("+V(p.value.gatewayBase)+")。 开启前自动备份原配置为 *.ellm-bak(保留最早的原始备份),关闭时从备份还原。 ",1),P.value?(d(),A(N,{key:0,title:P.value,type:k.value,"show-icon":"",class:"mb-8",onClose:a[1]||(a[1]=U=>P.value=null)},null,8,["title","type"])):T("",!0),s("div",be,[s("div",ke,[s("div",Ce,[a[8]||(a[8]=i(" Claude Code ",-1)),o(f,{type:p.value.claudeCode?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(p.value.claudeCode?"已开启":"未开启"),1)]),_:1},8,["type"])]),a[9]||(a[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))]),o(E,{"model-value":p.value.claudeCode,loading:u.value==="claudeCode",onChange:a[2]||(a[2]=U=>C("claudeCode",!!U))},null,8,["model-value","loading"])]),o(M,{class:"switch-divider"}),s("div",$e,[s("div",Ve,[s("div",xe,[a[10]||(a[10]=i(" Codex CLI ",-1)),o(f,{type:p.value.codex?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(p.value.codex?"已开启":"未开启"),1)]),_:1},8,["type"])]),a[11]||(a[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))]),o(E,{"model-value":p.value.codex,loading:u.value==="codex",onChange:a[3]||(a[3]=U=>C("codex",!!U))},null,8,["model-value","loading"])]),o(M,{class:"switch-divider"}),s("div",he,[s("div",Te,[s("div",Ae,[a[12]||(a[12]=i(" DeepSeek Harness ",-1)),o(f,{type:p.value.dsh?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(p.value.dsh?"已开启":"未开启"),1)]),_:1},8,["type"])]),a[13]||(a[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))]),o(E,{"model-value":p.value.dsh,loading:u.value==="dsh",onChange:a[4]||(a[4]=U=>C("dsh",!!U))},null,8,["model-value","loading"])])],64)):(d(),A(K,{key:1,description:S.value||"暂无开关状态"},{default:n(()=>[o($,{type:"primary",onClick:a[5]||(a[5]=U=>h(!1))},{default:n(()=>[...a[14]||(a[14]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1})),[[R,x.value]])}}}),Ee=ae(Se,[["__scopeId","data-v-5084d579"]]),Ue={class:"service-settings"},ze={class:"flex-between"},Be={key:0,class:"switch-row"},Le={class:"switch-info"},Ie={class:"switch-title"},Pe={key:0,class:"switch-row"},He={class:"switch-info"},Ke={class:"switch-title"},De={class:"switch-desc"},Ne={key:0,class:"version-block"},Oe={class:"version-row"},je={class:"muted-sm"},Me={key:0,class:"muted-sm"},Re={key:1,class:"muted-sm"},Fe={class:"muted-sm"},Ye={class:"version-actions"},We=ee({__name:"ServiceUpdateSettings",setup(ne){const{toast:y}=Z(),x=v(!0),z=v(!1),p=v(null),S=v(null),u=v(null),P=v(null),k=v(null),h=v(!1),B=v(!1),C=v(null);let g=null;de(()=>g==null?void 0:g.abort());const a=async l=>{var e,r;if(!l){if(z.value)return;z.value=!0}try{const[_,m]=await Promise.allSettled([L.autostart(),L.update()]);p.value=_.status==="fulfilled"?_.value:null,S.value=_.status==="rejected"?(e=_.reason)==null?void 0:e.message:null,u.value=m.status==="fulfilled"?m.value:null,P.value=m.status==="rejected"?(r=m.reason)==null?void 0:r.message:null}finally{x.value=!1,z.value=!1}};le(()=>a(!1));const $=j(()=>{var l,e;return((e=(l=u.value)==null?void 0:l.lastCheck)==null?void 0:e.currentVersion)??null}),N=j(()=>ye($.value)),f=j(()=>{var l,e;return((e=(l=u.value)==null?void 0:l.lastCheck)==null?void 0:e.latestVersion)??null}),E=j(()=>{var l,e;return((e=(l=u.value)==null?void 0:l.lastCheck)==null?void 0:e.hasUpdate)===!0}),M=j(()=>{var e,r;const l=(r=(e=u.value)==null?void 0:e.lastCheck)==null?void 0:r.checkedAt;return l?new Date(l).toLocaleString():null}),K=j(()=>{var r;const l=(r=u.value)==null?void 0:r.lastApply;if(!l)return null;const e=l.appliedAt?new Date(l.appliedAt).toLocaleString():"";return`${l.fromVersion} → ${l.toVersion}${e?`(${e})`:""}`}),F=l=>l?new Date(l).toLocaleString():"",R=async l=>{if(!p.value||k.value)return;const e=l?"确定开启开机自启?将注册登录触发的自启项(登录时自动恢复服务)。":"确定关闭开机自启?将移除自启项,且此后首次启动不再自动注册。";try{await q.confirm(e,l?"开启确认":"关闭确认",{confirmButtonText:l?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}k.value="autostart";try{const r=await L.setAutostart(l);r.note?y(r.note,"success"):y(l?"开机自启已开启":"开机自启已关闭","success")}catch(r){y(r.message||"操作失败","error")}finally{k.value=null,await a(!0)}},U=async l=>{if(!u.value||k.value)return;const e=l?"确定开启自动更新?将每小时检查 npm 源,发现新版本自动安装并重启服务。":"确定关闭自动更新?不再自动检查(手动检查更新不受影响)。";try{await q.confirm(e,l?"开启确认":"关闭确认",{confirmButtonText:l?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}k.value="updateEnabled";try{const r=await L.setUpdateEnabled(l);r.note&&y(r.note,"success")}catch(r){y(r.message||"操作失败","error")}finally{k.value=null,await a(!0)}},D=async()=>{h.value=!0;try{const l=await L.checkUpdate();C.value=l.hasUpdate?`发现新版本 ${l.latestVersion}(当前 ${l.currentVersion}):点击「立即更新」即可安装(服务自动重启,期间管理台短暂不可访问)`:`已是最新版本 ${l.currentVersion}(检查于 ${F(l.checkedAt)})`,await a(!0)}catch(l){y(l.message||"检查更新失败","error")}finally{h.value=!1}},t=async()=>{var e;try{await q.confirm(`确定更新到 ${f.value}?服务会自动重启并在启动时安装新版本:重启期间管理台短暂不可访问(预计 1~3 分钟),更新完成后自动恢复。`,"更新确认",{confirmButtonText:"立即更新",cancelButtonText:"取消",type:"warning"})}catch{return}B.value=!0;const l=new AbortController;g=l;try{const r=await L.update(),_=await L.applyUpdate();if(!_.updated){C.value=`已是最新版本 ${_.toVersion}`;return}C.value=`正在重启服务并安装 ${_.toVersion}(预计 1~3 分钟):重启期间管理台短暂不可访问,完成后这里会自动显示结果。`;const m=await fe({targetVersion:_.toVersion,sinceAppliedAt:((e=r.lastApply)==null?void 0:e.appliedAt)??null,signal:l.signal});if(l.signal.aborted)return;m&&!m.error?(y(`已更新到 ${m.toVersion}`,"success"),C.value=`已更新 ${m.fromVersion} → ${m.toVersion},服务已在新版本上运行。`):m!=null&&m.error?(y(`更新失败:${m.error}`,"error"),C.value=_e(m.error)):C.value="更新仍在进行(安装耗时较长):稍后刷新页面查看最新状态。"}catch(r){y(r.message||"更新失败","error")}finally{B.value=!1,g===l&&(g=null),l.signal.aborted||await a(!0)}};return(l,e)=>{const r=c("el-button"),_=c("el-tag"),m=c("el-switch"),Y=c("el-empty"),O=c("el-card"),W=c("el-link"),X=c("el-alert"),G=se("loading");return te((d(),b("div",Ue,[o(O,{shadow:"never",class:"mb-16"},{header:n(()=>[s("div",ze,[e[7]||(e[7]=s("span",{class:"fw-600"},"开机自启",-1)),o(r,{loading:z.value,onClick:e[0]||(e[0]=H=>a(!1))},{default:n(()=>[...e[6]||(e[6]=[i("刷新",-1)])]),_:1},8,["loading"])])]),default:n(()=>[p.value?(d(),b("div",Be,[s("div",Le,[s("div",Ie,[e[8]||(e[8]=i(" 登录时自动启动服务 ",-1)),o(_,{type:p.value.enabled?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(p.value.enabled?"已开启":"未开启"),1)]),_:1},8,["type"])]),e[9]||(e[9]=s("div",{class:"switch-desc"},[i(" 登录操作系统时自动启动服务(无需管理员权限)。 关闭后首次启动 "),s("code",null,"ezllm"),i(" 也不会再自动注册,重新开启即可解除。 ")],-1))]),o(m,{"model-value":p.value.enabled,loading:k.value==="autostart",onChange:e[1]||(e[1]=H=>R(!!H))},null,8,["model-value","loading"])])):(d(),A(Y,{key:1,description:S.value||"暂无自启状态"},{default:n(()=>[o(r,{type:"primary",onClick:e[2]||(e[2]=H=>a(!1))},{default:n(()=>[...e[10]||(e[10]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1}),o(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(()=>[u.value?(d(),b("div",Pe,[s("div",He,[s("div",Ke,[e[12]||(e[12]=i(" 自动检查并安装新版本 ",-1)),o(_,{type:u.value.enabled?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(u.value.enabled?"已开启":"未开启"),1)]),_:1},8,["type"])]),s("div",De,[u.value.supported?(d(),b(I,{key:1},[i(" 每小时检查 npm 源,发现新版本自动重启服务并在启动时安装(更新期间服务短暂中断); 关闭后仍可在下方手动检查与更新。 ")],64)):(d(),b(I,{key:0},[i(" 当前为源码运行模式,更新功能不可用(仅 npm 全局安装的包支持自动更新)。 ")],64))])]),u.value.supported?(d(),A(m,{key:0,"model-value":u.value.enabled,loading:k.value==="updateEnabled",onChange:e[3]||(e[3]=H=>U(!!H))},null,8,["model-value","loading"])):T("",!0)])):(d(),A(Y,{key:1,description:P.value||"暂无更新状态"},{default:n(()=>[o(r,{type:"primary",onClick:e[4]||(e[4]=H=>a(!1))},{default:n(()=>[...e[13]||(e[13]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1}),o(O,{shadow:"never"},{header:n(()=>[...e[14]||(e[14]=[s("div",{class:"flex-between"},[s("span",{class:"fw-600"},"版本信息")],-1)])]),default:n(()=>[u.value?(d(),b("div",Ne,[s("div",Oe,[e[18]||(e[18]=s("span",{class:"version-label"},"当前版本",-1)),o(_,{size:"small"},{default:n(()=>[i(V($.value||"未知"),1)]),_:1}),N.value?(d(),A(_,{key:0,type:"warning",size:"small","disable-transitions":""},{default:n(()=>[...e[15]||(e[15]=[i("本地构建",-1)])]),_:1})):T("",!0),f.value?(d(),b(I,{key:1},[e[17]||(e[17]=s("span",{class:"version-label"},"最新版本",-1)),o(_,{type:E.value?"warning":"success",size:"small","disable-transitions":""},{default:n(()=>[i(V(f.value),1)]),_:1},8,["type"]),E.value?(d(),A(_,{key:0,type:"warning",size:"small","disable-transitions":""},{default:n(()=>[...e[16]||(e[16]=[i("发现新版本",-1)])]),_:1})):T("",!0)],64)):T("",!0)]),s("div",je,[i(" 上次检查:"+V(M.value||"尚未检查")+" ",1),u.value.updating?(d(),b(I,{key:0},[i(" · 更新执行中...")],64)):T("",!0)]),u.value.supported?T("",!0):(d(),b("div",Me," 源码运行模式:手动检查与更新不可用。 ")),K.value?(d(),b("div",Re," 上次更新:"+V(K.value),1)):T("",!0),s("div",Fe,[e[20]||(e[20]=i(" npm 包主页:",-1)),o(W,{type:"primary",href:"https://www.npmjs.com/package/ezllm",target:"_blank"},{default:n(()=>[...e[19]||(e[19]=[i("npmjs.com/package/ezllm",-1)])]),_:1})]),s("div",Ye,[o(r,{loading:h.value,disabled:!u.value.supported||u.value.updating,onClick:D},{default:n(()=>[i(V(h.value?"检查中...":"检查更新"),1)]),_:1},8,["loading","disabled"]),E.value&&u.value.supported?(d(),A(r,{key:0,type:"primary",loading:B.value,disabled:u.value.updating,onClick:t},{default:n(()=>[i(V(B.value?"更新中...":`立即更新到 ${f.value}`),1)]),_:1},8,["loading","disabled"])):T("",!0)]),C.value?(d(),A(X,{key:2,title:C.value,type:"info","show-icon":"",class:"mt-8",onClose:e[5]||(e[5]=H=>C.value=null)},null,8,["title"])):T("",!0)])):(d(),A(Y,{key:1,description:"更新状态不可用"}))]),_:1})])),[[G,x.value]])}}}),Xe=ae(We,[["__scopeId","data-v-d51cfdf5"]]),qe={class:"flex-col-gap"},Ge={class:"flex-between"},Je={class:"muted-sm"},Qe={key:0},Ze={class:"login-row"},el={class:"muted-sm"},ll={class:"flex-between"},tl={class:"monitor-row"},sl={class:"save-bar"},al=ee({__name:"SettingsView",setup(ne){const{toast:y}=Z(),x=pe(),z=v("basic"),p=v(!0),S=v(!1),u=v(""),P=async()=>{if(!u.value)return;await me(u.value)?y("已复制到剪贴板","success"):y("复制失败","error")},k=v(29770),h=v(null),B=v(!0),C=v(null),g=v(null),a=v(""),$=v(""),N=j(()=>u.value.trim()!==""||a.value!==""&&$.value!==""),f=v(!1),E=v(60),M=[{label:"10分钟",value:10},{label:"30分钟",value:30},{label:"1小时",value:60},{label:"2小时",value:120},{label:"5小时",value:300},{label:"8小时",value:480},{label:"12小时",value:720},{label:"24小时",value:1440}],{loadError:K,run:F}=re("设置加载失败,请重试"),R=()=>F(async()=>{var D;try{await x.load(),u.value=x.config.masterKey||"",k.value=x.config.port??29770;const t=(D=x.config["web-ui"])==null?void 0:D["login-account"];a.value=(t==null?void 0:t.username)||"",$.value=(t==null?void 0:t.password)||"",B.value=x.config.allowLan!==!1;const l=x.config.latencyTest;f.value=(l==null?void 0:l.enabled)===!0,E.value=(l==null?void 0:l.intervalMinutes)??60;try{const e=await ve.get("/api/status");h.value=e.listenPort,C.value=e.listenHost??null,g.value=e.listenSource??null}catch{h.value=null,C.value=null,g.value=null}}finally{p.value=!1}}),U=async()=>{var t;S.value=!0;const D=f.value&&((t=x.config.latencyTest)==null?void 0:t.enabled)!==!0;try{const l=await x.update(e=>{var r;e.masterKey=u.value.trim(),e.port=k.value,e.allowLan=B.value,e.latencyTest={enabled:f.value,intervalMinutes:E.value},e["web-ui"]??(e["web-ui"]={}),(r=e["web-ui"])["login-account"]??(r["login-account"]={}),e["web-ui"]["login-account"].username=a.value.trim(),e["web-ui"]["login-account"].password=$.value});y("设置已保存并生效","success"),D&&y("已开启自动测试:服务将定期直连所有上游发送探测请求,有少量 token 消耗","warning"),l.restartHint&&y(l.restartHint,"warning")}catch(l){y(l.message,"error")}finally{S.value=!1}};return le(R),(D,t)=>{const l=c("el-button"),e=c("el-alert"),r=c("el-tag"),_=c("el-input"),m=c("el-form-item"),Y=c("el-input-number"),O=c("el-form"),W=c("el-card"),X=c("el-switch"),G=c("el-divider"),H=c("el-option"),oe=c("el-select"),J=c("el-tab-pane"),ie=c("el-tabs"),ue=se("loading");return d(),A(ie,{modelValue:z.value,"onUpdate:modelValue":t[7]||(t[7]=w=>z.value=w),class:"settings-tabs"},{default:n(()=>[o(J,{label:"基本设置",name:"basic"},{default:n(()=>[te((d(),b("div",qe,[Q(K)?(d(),A(e,{key:0,title:Q(K),type:"error","show-icon":"",closable:!1},{default:n(()=>[o(l,{size:"small",onClick:R},{default:n(()=>[...t[8]||(t[8]=[i("重试",-1)])]),_:1})]),_:1},8,["title"])):T("",!0),o(W,{shadow:"never"},{header:n(()=>[s("div",Ge,[t[10]||(t[10]=s("span",{class:"fw-600"},"网关访问",-1)),o(r,{type:"success"},{default:n(()=>[...t[9]||(t[9]=[i("运行中",-1)])]),_:1})])]),default:n(()=>[o(O,{"label-position":"top"},{default:n(()=>[o(m,{label:"API 密钥 (masterKey)"},{default:n(()=>[o(_,{modelValue:u.value,"onUpdate:modelValue":t[0]||(t[0]=w=>u.value=w),placeholder:"sk-1234",clearable:""},{append:n(()=>[o(l,{onClick:P},{default:n(()=>[...t[11]||(t[11]=[i("复制",-1)])]),_:1})]),_:1},8,["modelValue"]),t[12]||(t[12]=s("div",{class:"muted-sm"}," 客户端请求 /v1/* 时的认证密钥(Bearer <masterKey>;留空 = 本地放行) ",-1))]),_:1}),o(m,{label:"监听端口 (port)"},{default:n(()=>[o(Y,{modelValue:k.value,"onUpdate:modelValue":t[1]||(t[1]=w=>k.value=w),min:1,max:65535,step:1,style:{width:"220px"}},null,8,["modelValue"]),s("div",Je,[t[14]||(t[14]=i(" 网关与管理台 API 的监听端口,保存后重启生效 ",-1)),h.value!==null?(d(),b(I,{key:0},[t[13]||(t[13]=i(" ;当前实际监听 ",-1)),s("b",null,V(h.value),1),h.value!==k.value?(d(),b("span",Qe,"(由 PORT 环境变量覆盖,此配置值不生效)")):T("",!0)],64)):T("",!0)])]),_:1})]),_:1})]),_:1}),o(W,{shadow:"never"},{header:n(()=>[...t[15]||(t[15]=[s("span",{class:"fw-600"},"安全与登录",-1)])]),default:n(()=>[o(O,{"label-position":"top"},{default:n(()=>[o(m,{label:"管理台登录账号"},{default:n(()=>[s("div",Ze,[o(_,{modelValue:a.value,"onUpdate:modelValue":t[2]||(t[2]=w=>a.value=w),placeholder:"用户名",style:{width:"200px"}},null,8,["modelValue"]),o(_,{modelValue:$.value,"onUpdate:modelValue":t[3]||(t[3]=w=>$.value=w),type:"password",placeholder:"密码(至少 6 位)","show-password":"",style:{width:"200px"}},null,8,["modelValue"])]),t[16]||(t[16]=s("div",{class:"muted-sm"}," 管理台登录凭证;留空 = 免登录直接访问(仅本机使用时推荐)。 修改后立即生效,需用新账号重新登录 ",-1))]),_:1}),o(m,{label:"允许局域网访问 (allowLan)"},{default:n(()=>[o(X,{modelValue:B.value,"onUpdate:modelValue":t[4]||(t[4]=w=>B.value=w)},null,8,["modelValue"]),s("div",el,[t[19]||(t[19]=i(" 开启 = 绑定全部网卡,局域网内设备可访问;关闭 = 仅本机 127.0.0.1。保存后需重启生效 ",-1)),g.value==="env"?(d(),b(I,{key:0},[t[17]||(t[17]=i(" ;当前实际监听 ",-1)),s("b",null,V(C.value),1),t[18]||(t[18]=i("(由 HOST 环境变量覆盖,此开关不生效) ",-1))],64)):N.value?T("",!0):(d(),b(I,{key:1},[i(" ;未设置任何凭据(密钥与登录账号皆无)时始终仅绑本机 ")],64))])]),_:1})]),_:1})]),_:1}),o(W,{shadow:"never"},{header:n(()=>[s("div",ll,[t[20]||(t[20]=s("span",{class:"fw-600"},"模型服务监控",-1)),o(r,{type:f.value?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i(V(f.value?"已开启":"未开启"),1)]),_:1},8,["type"])])]),default:n(()=>[s("div",tl,[t[21]||(t[21]=s("div",{class:"monitor-info"},[s("div",{class:"monitor-title"},"自动测试模型服务状态"),s("div",{class:"muted-sm"}," 按设定间隔自动直连所有上游模型发送极小探测请求(max_tokens: 1,有少量 token 消耗); 测试结果展示在「服务状态」页面 ")],-1)),o(X,{modelValue:f.value,"onUpdate:modelValue":t[5]||(t[5]=w=>f.value=w)},null,8,["modelValue"])]),f.value?(d(),b(I,{key:0},[o(G,{style:{margin:"12px 0"}}),o(O,{"label-position":"top"},{default:n(()=>[o(m,{label:"测试间隔",style:{"margin-bottom":"0"}},{default:n(()=>[o(oe,{modelValue:E.value,"onUpdate:modelValue":t[6]||(t[6]=w=>E.value=w),style:{width:"220px"}},{default:n(()=>[(d(),b(I,null,ce(M,w=>o(H,{key:w.value,label:w.label,value:w.value},null,8,["label","value"])),64))]),_:1},8,["modelValue"]),t[22]||(t[22]=s("div",{class:"muted-sm"}," 自动测试的执行间隔,保存后即时生效(无需重启) ",-1))]),_:1})]),_:1})],64)):T("",!0)]),_:1}),s("div",sl,[o(l,{type:"primary",size:"large",loading:S.value,disabled:S.value||Q(K)!==null,onClick:U},{default:n(()=>[i(V(S.value?"保存中...":"保存设置"),1)]),_:1},8,["loading","disabled"])])])),[[ue,p.value]])]),_:1}),o(J,{label:"代理开关",name:"harness",lazy:""},{default:n(()=>[o(Ee)]),_:1}),o(J,{label:"服务与更新",name:"service",lazy:""},{default:n(()=>[o(Xe)]),_:1})]),_:1},8,["modelValue"])}}}),cl=ae(al,[["__scopeId","data-v-4c888be1"]]);export{cl as default};
@@ -1 +1 @@
1
- import{d as E,l as H,m as I,a6 as T,e as j,h as a,w as t,r as m,a as p,o as k,f as _,g as f,c as S,j as h,a3 as G,ah as w,A as K,aj as L,s as P,_ as R}from"./index-CgzCvxTY.js";import{c as Z}from"./clipboard-aCBIMfw7.js";const $={class:"setup-wrap"},q={class:"account-row"},z=E({__name:"SetupView",setup(D){const A=P(),o=m(!1),n=m("");let b=!1;const v=m(""),d=m(""),V=m(!1),u=m("");H(async()=>{!(await I()).masterKeySet&&o.value&&g()});function g(){n.value=B(),b=!0}T(o,l=>{l?n.value.trim()===""&&g():b&&(n.value="",b=!1)});function B(){const l=new Uint8Array(32);crypto.getRandomValues(l);let e="";for(const s of l)e+=String.fromCharCode(s);return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}async function U(){await Z(n.value)?w.success("已复制到剪贴板"):w.error("复制失败,请手动选择复制")}async function C(){u.value="";const l=o.value?"server":"local",e=n.value.trim(),s=v.value.trim()!==""||d.value!=="";if(o.value&&e===""){u.value="服务器部署必须设置主密钥,防止 /v1 网关被他人盗用";return}if(o.value&&!s){u.value="服务器部署必须设置登录账号密码,防止管理台被陌生人登录";return}if(s){if(v.value.trim()===""||d.value===""){u.value="用户名和密码需同时填写";return}if(d.value.length<6){u.value="密码至少 6 位";return}}V.value=!0;try{const r=e!==""?await K.post("/api/setup/master-key",{mode:"custom",masterKey:e}):null,y=await K.post("/api/setup/finish",{deployment:l,...s?{username:v.value.trim(),password:d.value}:{}});L(l,s),w.success(s?"初始化完成,请使用新账号登录":"初始化完成,免登录进入管理台");const c=(r==null?void 0:r.restartHint)??y.restartHint;c&&w({message:c,type:"warning",duration:8e3}),await A.push(s?"/login":"/")}catch(r){u.value=r.message||"保存失败"}finally{V.value=!1}}return(l,e)=>{const s=p("el-checkbox"),r=p("el-alert"),y=p("el-button"),c=p("el-input"),x=p("el-form-item"),M=p("el-form"),N=p("el-card");return k(),j("div",$,[a(N,{shadow:"hover",class:"setup-card"},{default:t(()=>[e[10]||(e[10]=_("h2",{class:"setup-title"},"初始化 EZLLM",-1)),a(s,{modelValue:o.value,"onUpdate:modelValue":e[0]||(e[0]=i=>o.value=i),class:"mb-16"},{default:t(()=>[...e[4]||(e[4]=[f(" 允许其他设备访问本服务(局域网 / 公网部署) ",-1)])]),_:1},8,["modelValue"]),o.value?(k(),S(r,{key:0,type:"warning","show-icon":"",closable:!1,title:"服务将对网络中的其他设备可见,请务必保留主密钥并修改默认账号",class:"mb-16"})):h("",!0),a(M,{"label-position":"top",onSubmit:G(C,["prevent"])},{default:t(()=>[a(x,null,{label:t(()=>[...e[5]||(e[5]=[f(" API 主密钥(本地部署可留空) ",-1),_("span",{class:"label-hint"},"调用 /v1/* 时的 Bearer Token;留空 = 不启用鉴权,且未设登录账号时服务仅本机可访问",-1)])]),default:t(()=>[a(c,{modelValue:n.value,"onUpdate:modelValue":e[1]||(e[1]=i=>n.value=i),placeholder:"已设置主密钥,留空保持不变",clearable:""},{append:t(()=>[a(y,{onClick:g},{default:t(()=>[...e[6]||(e[6]=[f("生成",-1)])]),_:1}),a(y,{disabled:!n.value,onClick:U},{default:t(()=>[...e[7]||(e[7]=[f("复制",-1)])]),_:1},8,["disabled"])]),_:1},8,["modelValue"])]),_:1}),a(x,null,{label:t(()=>[...e[8]||(e[8]=[f(" 管理台登录(可选) ",-1),_("span",{class:"label-hint"},"不填 = 免登录直接进管理台(纯本机使用);勾选上方后必须填写",-1)])]),default:t(()=>[_("div",q,[a(c,{modelValue:v.value,"onUpdate:modelValue":e[2]||(e[2]=i=>v.value=i),placeholder:"用户名",clearable:""},null,8,["modelValue"]),a(c,{modelValue:d.value,"onUpdate:modelValue":e[3]||(e[3]=i=>d.value=i),type:"password","show-password":"",placeholder:"密码(至少 6 位)"},null,8,["modelValue"])])]),_:1}),u.value?(k(),S(r,{key:0,title:u.value,type:"error","show-icon":"",closable:!1,class:"mb-16"},null,8,["title"])):h("",!0),a(y,{type:"primary",class:"full-width",loading:V.value,onClick:C},{default:t(()=>[...e[9]||(e[9]=[f(" 完成初始化 ",-1)])]),_:1},8,["loading"])]),_:1})]),_:1})])}}}),O=R(z,[["__scopeId","data-v-52af652f"]]);export{O as default};
1
+ import{d as E,l as H,m as I,a6 as T,e as G,h as a,w as t,r as m,a as p,o as k,f as _,g as f,c as S,j as h,a4 as L,af as w,A as K,ah as P,s as j,_ as R}from"./index-B2BlgF9y.js";import{c as Z}from"./clipboard-aCBIMfw7.js";const $={class:"setup-wrap"},q={class:"account-row"},z=E({__name:"SetupView",setup(D){const A=j(),o=m(!1),n=m("");let b=!1;const v=m(""),d=m(""),V=m(!1),u=m("");H(async()=>{!(await I()).masterKeySet&&o.value&&g()});function g(){n.value=B(),b=!0}T(o,l=>{l?n.value.trim()===""&&g():b&&(n.value="",b=!1)});function B(){const l=new Uint8Array(32);crypto.getRandomValues(l);let e="";for(const s of l)e+=String.fromCharCode(s);return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}async function U(){await Z(n.value)?w.success("已复制到剪贴板"):w.error("复制失败,请手动选择复制")}async function C(){u.value="";const l=o.value?"server":"local",e=n.value.trim(),s=v.value.trim()!==""||d.value!=="";if(o.value&&e===""){u.value="服务器部署必须设置主密钥,防止 /v1 网关被他人盗用";return}if(o.value&&!s){u.value="服务器部署必须设置登录账号密码,防止管理台被陌生人登录";return}if(s){if(v.value.trim()===""||d.value===""){u.value="用户名和密码需同时填写";return}if(d.value.length<6){u.value="密码至少 6 位";return}}V.value=!0;try{const r=e!==""?await K.post("/api/setup/master-key",{mode:"custom",masterKey:e}):null,y=await K.post("/api/setup/finish",{deployment:l,...s?{username:v.value.trim(),password:d.value}:{}});P(l,s),w.success(s?"初始化完成,请使用新账号登录":"初始化完成,免登录进入管理台");const c=(r==null?void 0:r.restartHint)??y.restartHint;c&&w({message:c,type:"warning",duration:8e3}),await A.push(s?"/login":"/")}catch(r){u.value=r.message||"保存失败"}finally{V.value=!1}}return(l,e)=>{const s=p("el-checkbox"),r=p("el-alert"),y=p("el-button"),c=p("el-input"),x=p("el-form-item"),M=p("el-form"),N=p("el-card");return k(),G("div",$,[a(N,{shadow:"hover",class:"setup-card"},{default:t(()=>[e[10]||(e[10]=_("h2",{class:"setup-title"},"初始化 EZLLM",-1)),a(s,{modelValue:o.value,"onUpdate:modelValue":e[0]||(e[0]=i=>o.value=i),class:"mb-16"},{default:t(()=>[...e[4]||(e[4]=[f(" 允许其他设备访问本服务(局域网 / 公网部署) ",-1)])]),_:1},8,["modelValue"]),o.value?(k(),S(r,{key:0,type:"warning","show-icon":"",closable:!1,title:"服务将对网络中的其他设备可见,请务必保留主密钥并修改默认账号",class:"mb-16"})):h("",!0),a(M,{"label-position":"top",onSubmit:L(C,["prevent"])},{default:t(()=>[a(x,null,{label:t(()=>[...e[5]||(e[5]=[f(" API 主密钥(本地部署可留空) ",-1),_("span",{class:"label-hint"},"调用 /v1/* 时的 Bearer Token;留空 = 不启用鉴权,且未设登录账号时服务仅本机可访问",-1)])]),default:t(()=>[a(c,{modelValue:n.value,"onUpdate:modelValue":e[1]||(e[1]=i=>n.value=i),placeholder:"已设置主密钥,留空保持不变",clearable:""},{append:t(()=>[a(y,{onClick:g},{default:t(()=>[...e[6]||(e[6]=[f("生成",-1)])]),_:1}),a(y,{disabled:!n.value,onClick:U},{default:t(()=>[...e[7]||(e[7]=[f("复制",-1)])]),_:1},8,["disabled"])]),_:1},8,["modelValue"])]),_:1}),a(x,null,{label:t(()=>[...e[8]||(e[8]=[f(" 管理台登录(可选) ",-1),_("span",{class:"label-hint"},"不填 = 免登录直接进管理台(纯本机使用);勾选上方后必须填写",-1)])]),default:t(()=>[_("div",q,[a(c,{modelValue:v.value,"onUpdate:modelValue":e[2]||(e[2]=i=>v.value=i),placeholder:"用户名",clearable:""},null,8,["modelValue"]),a(c,{modelValue:d.value,"onUpdate:modelValue":e[3]||(e[3]=i=>d.value=i),type:"password","show-password":"",placeholder:"密码(至少 6 位)"},null,8,["modelValue"])])]),_:1}),u.value?(k(),S(r,{key:0,title:u.value,type:"error","show-icon":"",closable:!1,class:"mb-16"},null,8,["title"])):h("",!0),a(y,{type:"primary",class:"full-width",loading:V.value,onClick:C},{default:t(()=>[...e[9]||(e[9]=[f(" 完成初始化 ",-1)])]),_:1},8,["loading"])]),_:1})]),_:1})])}}}),O=R(z,[["__scopeId","data-v-52af652f"]]);export{O as default};
@@ -1 +1 @@
1
- import{a1 as p,r as s,p as n,A as i}from"./index-CgzCvxTY.js";const y=p("config",()=>{const a=s({}),e=s(!1),o=n(()=>Array.isArray(a.value.providers)?a.value.providers:[]),l=n(()=>o.value.length),c=n(()=>o.value.reduce((t,d)=>{var u;return t+(((u=d.models)==null?void 0:u.length)??0)},0));async function f(){e.value=!0;try{a.value=await i.get("/api/config")}finally{e.value=!1}}async function r(){return await i.put("/api/config",a.value)}async function v(t){return t(a.value),await r()}return{config:a,loading:e,providers:o,providerCount:l,totalModelCount:c,load:f,save:r,update:v}});export{y as u};
1
+ import{a1 as p,r as s,p as n,A as i}from"./index-B2BlgF9y.js";const y=p("config",()=>{const a=s({}),e=s(!1),o=n(()=>Array.isArray(a.value.providers)?a.value.providers:[]),l=n(()=>o.value.length),c=n(()=>o.value.reduce((t,d)=>{var u;return t+(((u=d.models)==null?void 0:u.length)??0)},0));async function f(){e.value=!0;try{a.value=await i.get("/api/config")}finally{e.value=!1}}async function r(){return await i.put("/api/config",a.value)}async function v(t){return t(a.value),await r()}return{config:a,loading:e,providers:o,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-CK_MaWN7.js","assets/useManagerApi-CBQNomtR.js","assets/useToast-DNMhOOeI.js","assets/version-BKHjE_LF.js","assets/LayoutView-kZ2yGen8.css","assets/DashboardView-fRDkGq0g.js","assets/providers-i7t_wEzk.js","assets/config-CdOq1p3C.js","assets/useLoadError-UA3r7sR9.js","assets/format-kTdM4RVv.js","assets/clipboard-aCBIMfw7.js","assets/DashboardView-DwlQExQv.css","assets/ProvidersView-DWtS_e7t.js","assets/ProvidersView-Caqpg_0F.css","assets/ServiceStatusView-D3ybS2T-.js","assets/ServiceStatusView-DrPoYMN-.css","assets/SettingsView-DepOaZYg.js","assets/SettingsView-D1kbH5Ev.css","assets/LogsView-YtH8gJPU.js","assets/LogsView-CCjJTjvZ.css","assets/LoginView-B1naaT8E.js","assets/LoginView-CSfb59pE.css","assets/SetupView-BwDwokAd.js","assets/SetupView-pS9nSnLg.css"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/LayoutView-Div_5EBT.js","assets/useManagerApi-CnyeOfgT.js","assets/useToast-Cve-kXIC.js","assets/version-BKHjE_LF.js","assets/LayoutView-kZ2yGen8.css","assets/DashboardView-BE5e2n6u.js","assets/providers-ABdiyjVl.js","assets/config-D_yFz2xb.js","assets/useLoadError-Vo8GaucY.js","assets/format-kTdM4RVv.js","assets/clipboard-aCBIMfw7.js","assets/DashboardView-DwlQExQv.css","assets/ProvidersView-CTqR-k3L.js","assets/ProvidersView-0jNbWJE5.css","assets/ServiceStatusView-D1pXtHQJ.js","assets/ServiceStatusView-DN0AwyLo.css","assets/SettingsView--ZDLNPOa.js","assets/SettingsView-D1kbH5Ev.css","assets/LogsView-CNop4N9G.js","assets/LogsView-CCjJTjvZ.css","assets/LoginView-cCi4Iwct.js","assets/LoginView-CSfb59pE.css","assets/SetupView-BqrEqw3F.js","assets/SetupView-pS9nSnLg.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 cfe=()=>location.protocol+"//"+location.host;function f9(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),d3(s,"")}return d3(n,e)+a+l}function dfe(e,t,n,a){let l=[],o=[],r=null;const s=({state:p})=>{const m=f9(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 m3(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 ffe(e){const{history:t,location:n}=window,a={value:f9(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:cfe()+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,m3(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({},m3(a.value,u,null),{position:d.position+1},c),!1),a.value=u}return{location:a,state:l,push:s,replace:r}}function pfe(e){e=Xde(e);const t=ffe(e),n=dfe(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:Jde.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 hfe(e){return e=location.host?e||location.pathname+location.search:"",e.includes("#")||(e+="#"),pfe(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 vfe={type:Tr.Static,value:""},mfe=/[a-zA-Z0-9_]/;function gfe(e){if(!e)return[[]];if(e==="/")return[[vfe]];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:mfe.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 g3="[^/]+?",yfe={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 bfe=/[.+*?^${}()[\]/\\]/g;function wfe(e,t){const n=cn({},yfe,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(bfe,"\\$&"),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||g3;if(b!==g3){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 _fe(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 p9(e,t){let n=0;const a=e.score,l=t.score;for(;n<a.length&&n<l.length;){const o=_fe(a[n],l[n]);if(o)return o;n++}if(Math.abs(l.length-a.length)===1){if(y3(a))return 1;if(y3(l))return-1}return l.length-a.length}function y3(e){const t=e[e.length-1];return e.length>0&&t[t.length-1]<0}const Cfe={strict:!1,end:!0,sensitive:!1};function Sfe(e,t,n){const a=wfe(gfe(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 xfe(e,t){const n=[],a=new Map;t=c3(Cfe,t);function l(f){return a.get(f)}function o(f,p,m){const v=!m,h=w3(f);h.aliasOf=m&&m.record;const g=c3(t,f),y=[h];if("alias"in f){const _=typeof f.alias=="string"?[f.alias]:f.alias;for(const C of _)y.push(w3(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=Sfe(_,p,g),m?m.alias.push(b):(w=w||b,w!==b&&w.alias.push(b),v&&f.name&&!_3(b)&&r(f.name)),h9(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(c9(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=Tfe(f,n);n.splice(p,0,f),f.record.name&&!_3(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(b3(p.params,m.keys.filter(w=>!w.optional).concat(m.parent?m.parent.keys.filter(w=>w.optional):[]).map(w=>w.name)),f.params&&b3(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:Efe(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 b3(e,t){const n={};for(const a of t)a in e&&(n[a]=e[a]);return n}function w3(e){const t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:kfe(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 kfe(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 _3(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function Efe(e){return e.reduce((t,n)=>cn(t,n.meta),{})}function Tfe(e,t){let n=0,a=t.length;for(;n!==a;){const o=n+a>>1;p9(e,t[o])<0?a=o:n=o+1}const l=Mfe(e);return l&&(a=t.lastIndexOf(l,a-1)),a}function Mfe(e){let t=e;for(;t=t.parent;)if(h9(t)&&p9(e,t)===0)return t}function h9({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function C3(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=S3(u[c-2]);return c>1&&S3(d)===m&&f[f.length-1].path!==m?f.findIndex(ai.bind(null,u[c-2])):p}),o=x(()=>l.value>-1&&Rfe(n.params,a.value.params)),r=x(()=>l.value>-1&&l.value===n.matched.length-1&&u9(n.params,a.value.params));function s(u={}){if($fe(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 Ofe(e){return e.length===1?e[0]:e}const Nfe=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:C3,setup(e,{slots:t}){const n=Dt(C3(e)),{options:a}=Le(b0),l=x(()=>({[x3(e.activeClass,a.linkActiveClass,"router-link-active")]:n.isActive,[x3(e.exactActiveClass,a.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const o=t.default&&Ofe(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)}}}),Afe=Nfe;function $fe(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 Rfe(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 S3(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const x3=(e,t,n)=>e??t??n,Pfe=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(v3,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(v3,x(()=>r.value+1)),St(ife,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 k3(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 k3(n.default,{Component:g,route:c})||g}}});function k3(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Ife=Pfe;function Lfe(e){const t=xfe(e.routes,e),n=e.parseQuery||rfe,a=e.stringifyQuery||h3,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,Kde),p=mp.bind(null,Ru);function m(Q,ce){let de,Y;return c9(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:Ru(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=qde(a,cn({},Q,{hash:Dde(Z),path:Y.path})),j=l.createHref(ie);return cn({fullPath:ie,hash:Z,query:a===h3?sfe(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&&Ude(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]=ufe(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&&tfe(p3(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&&nfe(p3(Q.fullPath,0))||(Y||!de)&&history.state&&history.state.scroll||null;return Re().then(()=>Z(Q,ce,ie)).then(j=>j&&efe(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",Afe),Q.component("RouterView",Ife),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,Du(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 Xfe(){return Le(b0)}function Zfe(e){return Le(f1)}var E3;const Vfe=((E3=window.__ELLM_CONFIG__)==null?void 0:E3.API_BASE)??"",Bfe=3e4,p1="webui_token";let Qi=null;function zfe(){return Qi===null&&(Qi=localStorage.getItem(p1)),Qi}function Jfe(e){Qi=e,localStorage.setItem(p1,e)}function Dfe(){Qi=null,localStorage.removeItem(p1)}function Hfe(e){if(!Array.isArray(e)||e.length===0)return"";const t=e.map(n=>{if(typeof n!="object"||n===null)return String(n);const a=n,l=a.path===void 0?"":String(a.path),o=a.message===void 0?"":String(a.message);return l===""?o:o===""?l:`${l}: ${o}`}).filter(n=>n.trim()!=="");return t.length===0?"":`——${t.join(";")}`}function Ffe(){Dfe(),h1.push("/login")}async function Jc(e,t,n){const a={"Content-Type":"application/json"},l=zfe();l&&(a.Authorization=`Bearer ${l}`);const o=new AbortController,r=setTimeout(()=>o.abort(),Bfe);try{const s=await fetch(`${Vfe}${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||"用户名或密码错误"):(Ffe(),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||c.ok===!1)throw new Error(`${String(c.error||`HTTP ${s.status}`)}${Hfe(c.issues)}`);return c}catch(s){throw s.name==="AbortError"?new Error("请求超时"):s instanceof TypeError?new Error("服务不可达(未启动或更新重启窗口期,请稍后重试)"):s}finally{clearTimeout(r)}}const Kfe={get:e=>Jc("GET",e),post:(e,t)=>Jc("POST",e,t),put:(e,t)=>Jc("PUT",e,t),del:e=>Jc("DELETE",e)},Wfe=Kfe,xs=B(null);async function jfe(e=!1){if(xs.value&&!e)return xs.value;try{xs.value=await Wfe.get("/api/setup/status")}catch{xs.value={initialized:!0,masterKeySet:!0,authRequired:!0}}return xs.value}function Qfe(e,t){xs.value={initialized:!0,deployment:e,masterKeySet:!0,authRequired:t}}const h1=Lfe({history:hfe(),routes:[{path:"/",component:()=>Bo(()=>import("./LayoutView-CK_MaWN7.js"),__vite__mapDeps([0,1,2,3,4])),children:[{path:"",name:"Dashboard",component:()=>Bo(()=>import("./DashboardView-fRDkGq0g.js"),__vite__mapDeps([5,1,6,7,2,8,9,10,11]))},{path:"providers",name:"Providers",component:()=>Bo(()=>import("./ProvidersView-DWtS_e7t.js"),__vite__mapDeps([12,2,6,7,8,10,13]))},{path:"service-status",name:"ServiceStatus",component:()=>Bo(()=>import("./ServiceStatusView-D3ybS2T-.js"),__vite__mapDeps([14,2,8,10,15]))},{path:"settings",name:"Settings",component:()=>Bo(()=>import("./SettingsView-DepOaZYg.js"),__vite__mapDeps([16,2,8,7,10,1,3,17]))},{path:"logs",name:"Logs",component:()=>Bo(()=>import("./LogsView-YtH8gJPU.js"),__vite__mapDeps([18,6,7,2,8,10,9,19]))}]},{path:"/login",name:"Login",component:()=>Bo(()=>import("./LoginView-B1naaT8E.js"),__vite__mapDeps([20,21]))},{path:"/setup",name:"Setup",component:()=>Bo(()=>import("./SetupView-BwDwokAd.js"),__vite__mapDeps([22,10,23]))},{path:"/:pathMatch(.*)*",redirect:"/"}]});h1.beforeEach(async e=>{const t=await jfe();if(!t.initialized)return e.name==="Setup"?!0:{name:"Setup"};if(e.name==="Setup")return{name:"Dashboard"};if(t.authRequired===!1)return e.name==="Login"?{name:"Dashboard"}:!0;const n=localStorage.getItem("webui_token");if(e.name!=="Login"&&!n)return{name:"Login"}});const wc=du(Tde);wc.use(hk());wc.use(Cde,{locale:Sde});wc.use(h1);for(const[e,t]of Object.entries(uK))wc.component(e,t);wc.mount("#app");export{qfe as $,Wfe as A,sn as B,Ie as C,Z2 as D,rde as E,Fe as F,Mn as G,$a as H,b9 as I,Qr as J,en as K,st as L,vo as M,dt as N,ge as O,oi as P,er as Q,W9 as R,H9 as S,F9 as T,K9 as U,O3 as V,Ra as W,$9 as X,Gfe as Y,Ufe as Z,xde as _,Kt as a,to as a0,Yfe as a1,Dt as a2,Qe as a3,P as a4,_D as a5,fe as a6,M8 as a7,rn as a8,oL as a9,NF as aa,C8 as ab,iv as ac,Re as ad,It as ae,Ye as af,Kfe as ag,qce as ah,Jfe as ai,Qfe as aj,ht as b,ue as c,K as d,A as e,O as f,bt as g,J as h,i,re as j,ph as k,gt as l,jfe as m,El as n,S as o,x as p,Dfe as q,B as r,Xfe as s,Se as t,HS as u,Zfe as v,ae as w,xs as x,To as y,_t as z};
96
+ */let cfe=()=>location.protocol+"//"+location.host;function f9(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),d3(s,"")}return d3(n,e)+a+l}function dfe(e,t,n,a){let l=[],o=[],r=null;const s=({state:p})=>{const m=f9(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 m3(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 ffe(e){const{history:t,location:n}=window,a={value:f9(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:cfe()+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,m3(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({},m3(a.value,u,null),{position:d.position+1},c),!1),a.value=u}return{location:a,state:l,push:s,replace:r}}function pfe(e){e=Xde(e);const t=ffe(e),n=dfe(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:Jde.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 hfe(e){return e=location.host?e||location.pathname+location.search:"",e.includes("#")||(e+="#"),pfe(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 vfe={type:Tr.Static,value:""},mfe=/[a-zA-Z0-9_]/;function gfe(e){if(!e)return[[]];if(e==="/")return[[vfe]];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:mfe.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 g3="[^/]+?",yfe={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 bfe=/[.+*?^${}()[\]/\\]/g;function wfe(e,t){const n=cn({},yfe,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(bfe,"\\$&"),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||g3;if(b!==g3){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 _fe(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 p9(e,t){let n=0;const a=e.score,l=t.score;for(;n<a.length&&n<l.length;){const o=_fe(a[n],l[n]);if(o)return o;n++}if(Math.abs(l.length-a.length)===1){if(y3(a))return 1;if(y3(l))return-1}return l.length-a.length}function y3(e){const t=e[e.length-1];return e.length>0&&t[t.length-1]<0}const Cfe={strict:!1,end:!0,sensitive:!1};function Sfe(e,t,n){const a=wfe(gfe(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 xfe(e,t){const n=[],a=new Map;t=c3(Cfe,t);function l(f){return a.get(f)}function o(f,p,m){const v=!m,h=w3(f);h.aliasOf=m&&m.record;const g=c3(t,f),y=[h];if("alias"in f){const _=typeof f.alias=="string"?[f.alias]:f.alias;for(const C of _)y.push(w3(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=Sfe(_,p,g),m?m.alias.push(b):(w=w||b,w!==b&&w.alias.push(b),v&&f.name&&!_3(b)&&r(f.name)),h9(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(c9(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=Tfe(f,n);n.splice(p,0,f),f.record.name&&!_3(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(b3(p.params,m.keys.filter(w=>!w.optional).concat(m.parent?m.parent.keys.filter(w=>w.optional):[]).map(w=>w.name)),f.params&&b3(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:Efe(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 b3(e,t){const n={};for(const a of t)a in e&&(n[a]=e[a]);return n}function w3(e){const t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:kfe(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 kfe(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 _3(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function Efe(e){return e.reduce((t,n)=>cn(t,n.meta),{})}function Tfe(e,t){let n=0,a=t.length;for(;n!==a;){const o=n+a>>1;p9(e,t[o])<0?a=o:n=o+1}const l=Mfe(e);return l&&(a=t.lastIndexOf(l,a-1)),a}function Mfe(e){let t=e;for(;t=t.parent;)if(h9(t)&&p9(e,t)===0)return t}function h9({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function C3(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=S3(u[c-2]);return c>1&&S3(d)===m&&f[f.length-1].path!==m?f.findIndex(ai.bind(null,u[c-2])):p}),o=x(()=>l.value>-1&&Rfe(n.params,a.value.params)),r=x(()=>l.value>-1&&l.value===n.matched.length-1&&u9(n.params,a.value.params));function s(u={}){if($fe(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 Ofe(e){return e.length===1?e[0]:e}const Nfe=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:C3,setup(e,{slots:t}){const n=Dt(C3(e)),{options:a}=Le(b0),l=x(()=>({[x3(e.activeClass,a.linkActiveClass,"router-link-active")]:n.isActive,[x3(e.exactActiveClass,a.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const o=t.default&&Ofe(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)}}}),Afe=Nfe;function $fe(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 Rfe(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 S3(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const x3=(e,t,n)=>e??t??n,Pfe=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(v3,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(v3,x(()=>r.value+1)),St(ife,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 k3(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 k3(n.default,{Component:g,route:c})||g}}});function k3(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Ife=Pfe;function Lfe(e){const t=xfe(e.routes,e),n=e.parseQuery||rfe,a=e.stringifyQuery||h3,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,Kde),p=mp.bind(null,Ru);function m(Q,ce){let de,Y;return c9(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:Ru(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=qde(a,cn({},Q,{hash:Dde(Z),path:Y.path})),j=l.createHref(ie);return cn({fullPath:ie,hash:Z,query:a===h3?sfe(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&&Ude(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]=ufe(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&&tfe(p3(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&&nfe(p3(Q.fullPath,0))||(Y||!de)&&history.state&&history.state.scroll||null;return Re().then(()=>Z(Q,ce,ie)).then(j=>j&&efe(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",Afe),Q.component("RouterView",Ife),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,Du(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 Xfe(){return Le(b0)}function Zfe(e){return Le(f1)}var E3;const Vfe=((E3=window.__ELLM_CONFIG__)==null?void 0:E3.API_BASE)??"",Bfe=3e4,p1="webui_token";let Qi=null;function zfe(){return Qi===null&&(Qi=localStorage.getItem(p1)),Qi}function Jfe(e){Qi=e,localStorage.setItem(p1,e)}function Dfe(){Qi=null,localStorage.removeItem(p1)}function Hfe(e){if(!Array.isArray(e)||e.length===0)return"";const t=e.map(n=>{if(typeof n!="object"||n===null)return String(n);const a=n,l=a.path===void 0?"":String(a.path),o=a.message===void 0?"":String(a.message);return l===""?o:o===""?l:`${l}: ${o}`}).filter(n=>n.trim()!=="");return t.length===0?"":`——${t.join(";")}`}function Ffe(){Dfe(),h1.push("/login")}async function Jc(e,t,n){const a={"Content-Type":"application/json"},l=zfe();l&&(a.Authorization=`Bearer ${l}`);const o=new AbortController,r=setTimeout(()=>o.abort(),Bfe);try{const s=await fetch(`${Vfe}${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||"用户名或密码错误"):(Ffe(),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||c.ok===!1)throw new Error(`${String(c.error||`HTTP ${s.status}`)}${Hfe(c.issues)}`);return c}catch(s){throw s.name==="AbortError"?new Error("请求超时"):s instanceof TypeError?new Error("服务不可达(未启动或更新重启窗口期,请稍后重试)"):s}finally{clearTimeout(r)}}const Kfe={get:e=>Jc("GET",e),post:(e,t)=>Jc("POST",e,t),put:(e,t)=>Jc("PUT",e,t),del:e=>Jc("DELETE",e)},Wfe=Kfe,xs=B(null);async function jfe(e=!1){if(xs.value&&!e)return xs.value;try{xs.value=await Wfe.get("/api/setup/status")}catch{xs.value={initialized:!0,masterKeySet:!0,authRequired:!0}}return xs.value}function Qfe(e,t){xs.value={initialized:!0,deployment:e,masterKeySet:!0,authRequired:t}}const h1=Lfe({history:hfe(),routes:[{path:"/",component:()=>Bo(()=>import("./LayoutView-Div_5EBT.js"),__vite__mapDeps([0,1,2,3,4])),children:[{path:"",name:"Dashboard",component:()=>Bo(()=>import("./DashboardView-BE5e2n6u.js"),__vite__mapDeps([5,1,6,7,2,8,9,10,11]))},{path:"providers",name:"Providers",component:()=>Bo(()=>import("./ProvidersView-CTqR-k3L.js"),__vite__mapDeps([12,2,6,7,8,10,13]))},{path:"service-status",name:"ServiceStatus",component:()=>Bo(()=>import("./ServiceStatusView-D1pXtHQJ.js"),__vite__mapDeps([14,2,8,10,15]))},{path:"settings",name:"Settings",component:()=>Bo(()=>import("./SettingsView--ZDLNPOa.js"),__vite__mapDeps([16,2,8,7,10,1,3,17]))},{path:"logs",name:"Logs",component:()=>Bo(()=>import("./LogsView-CNop4N9G.js"),__vite__mapDeps([18,6,7,2,8,10,9,19]))}]},{path:"/login",name:"Login",component:()=>Bo(()=>import("./LoginView-cCi4Iwct.js"),__vite__mapDeps([20,21]))},{path:"/setup",name:"Setup",component:()=>Bo(()=>import("./SetupView-BqrEqw3F.js"),__vite__mapDeps([22,10,23]))},{path:"/:pathMatch(.*)*",redirect:"/"}]});h1.beforeEach(async e=>{const t=await jfe();if(!t.initialized)return e.name==="Setup"?!0:{name:"Setup"};if(e.name==="Setup")return{name:"Dashboard"};if(t.authRequired===!1)return e.name==="Login"?{name:"Dashboard"}:!0;const n=localStorage.getItem("webui_token");if(e.name!=="Login"&&!n)return{name:"Login"}});const wc=du(Tde);wc.use(hk());wc.use(Cde,{locale:Sde});wc.use(h1);for(const[e,t]of Object.entries(uK))wc.component(e,t);wc.mount("#app");export{qfe as $,Wfe as A,sn as B,Ie as C,Z2 as D,rde as E,Fe as F,Mn as G,$a as H,b9 as I,Qr as J,en as K,st as L,vo as M,dt as N,ge as O,oi as P,er as Q,W9 as R,H9 as S,F9 as T,K9 as U,O3 as V,Ra as W,$9 as X,Gfe as Y,Ufe as Z,xde as _,Kt as a,to as a0,Yfe as a1,Dt as a2,P as a3,Qe as a4,_D as a5,fe as a6,M8 as a7,rn as a8,oL as a9,NF as aa,C8 as ab,iv as ac,Ye as ad,Kfe as ae,qce as af,Jfe as ag,Qfe as ah,ht as b,ue as c,K as d,A as e,O as f,bt as g,J as h,i,re as j,ph as k,gt as l,jfe as m,El as n,S as o,x as p,Dfe as q,B as r,Xfe as s,Se as t,HS as u,Zfe as v,ae as w,xs as x,To as y,_t as z};
@@ -0,0 +1 @@
1
+ import{a1 as C,r as u,p as l}from"./index-B2BlgF9y.js";import{u as N}from"./config-D_yFz2xb.js";const J=C("providers",()=>{const s=N(),v=u(!1),n=l(()=>s.providers),f=l(()=>s.loading),m=l(()=>n.value.length),p=l(()=>n.value.reduce((e,o)=>{var t;return e+(((t=o.models)==null?void 0:t.length)??0)},0)),a=u([]);function g(){try{const e=localStorage.getItem("provider_order");e&&(a.value=JSON.parse(e))}catch{}}function d(){try{localStorage.setItem("provider_order",JSON.stringify(a.value))}catch{}}const S=l(()=>{const e=a.value.map(r=>n.value.find(i=>i.name===r)).filter(r=>!!r);for(const r of n.value)e.find(i=>i.name===r.name)||e.push(r);const o=e.map(r=>r.name);(o.length!==a.value.length||o.some((r,i)=>a.value[i]!==r))&&(a.value=o,d());const t=r=>r.enabled!==!1;return[...e.filter(t),...e.filter(r=>!t(r))]});function h(e){a.value=e.map(o=>o.name),d()}function y(e){a.value=[e,...a.value.filter(o=>o!==e)],d()}async function P(){g(),await s.load()}async function c(e){return s.config.providers=e,await s.save()}async function O(e){const o=n.value.filter(t=>t.name!==e.name);return await c(o)}async function w(e){const o=n.value.map(t=>({...t,models:(t.models??[]).filter(r=>r.modelName!==e)}));return await c(o)}return{suppliers:n,loading:f,restarting:v,orderedProviders:S,providerCount:m,totalModelCount:p,loadProviders:P,saveProviders:c,deleteProvider:O,deleteModel:w,reorderProviders:h,moveProviderToTop:y}});export{J as u};
@@ -1 +1 @@
1
- import{r as e}from"./index-CgzCvxTY.js";function l(a="加载失败,请稍后重试"){const r=e(null);async function n(t){r.value=null;try{return await t(),!0}catch(u){return r.value=(u==null?void 0:u.message)||a,!1}}return{loadError:r,run:n}}export{l as u};
1
+ import{r as e}from"./index-B2BlgF9y.js";function l(a="加载失败,请稍后重试"){const r=e(null);async function n(t){r.value=null;try{return await t(),!0}catch(u){return r.value=(u==null?void 0:u.message)||a,!1}}return{loadError:r,run:n}}export{l as u};
@@ -1 +1 @@
1
- import{ag as e}from"./index-CgzCvxTY.js";const c={status:()=>e.get("/status"),processes:()=>e.get("/processes").then(t=>t.processes),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 h(t){const{targetVersion:r,sinceAppliedAt:n,timeoutMs:u=600*1e3,pollMs:d=3e3,signal:s}=t,o=Date.now()+u;for(;;){if(s!=null&&s.aborted||Date.now()>=o||(await new Promise(l=>setTimeout(l,d)),s!=null&&s.aborted))return null;let p=null;try{p=await c.update()}catch{continue}const a=p.lastApply;if(a&&a.toVersion===r&&(a.appliedAt??0)>(n??0))return a;if(Date.now()>=o)return null}}export{c as m,h as w};
1
+ import{ae as e}from"./index-B2BlgF9y.js";const c={status:()=>e.get("/status"),processes:()=>e.get("/processes").then(t=>t.processes),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 h(t){const{targetVersion:r,sinceAppliedAt:n,timeoutMs:u=600*1e3,pollMs:d=3e3,signal:s}=t,o=Date.now()+u;for(;;){if(s!=null&&s.aborted||Date.now()>=o||(await new Promise(l=>setTimeout(l,d)),s!=null&&s.aborted))return null;let p=null;try{p=await c.update()}catch{continue}const a=p.lastApply;if(a&&a.toVersion===r&&(a.appliedAt??0)>(n??0))return a;if(Date.now()>=o)return null}}export{c as m,h as w};
@@ -1 +1 @@
1
- import{ah as o}from"./index-CgzCvxTY.js";function e(){function a(n,s="success"){o({message:n,type:s,duration:s==="warning"?8e3:3e3})}return{toast:a}}export{e as u};
1
+ import{af as o}from"./index-B2BlgF9y.js";function e(){function a(n,s="success"){o({message:n,type:s,duration:s==="warning"?8e3:3e3})}return{toast:a}}export{e as u};
@@ -6,7 +6,7 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>EZLLM - 网关管理台</title>
8
8
  <script src="/config.js"></script>
9
- <script type="module" crossorigin src="/assets/index-CgzCvxTY.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-B2BlgF9y.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-BecfPmag.css">
11
11
  </head>
12
12
  <body>
package/docs/DESIGN.md CHANGED
@@ -1,16 +1,16 @@
1
- # ellm-proxy 发布包设计文档
1
+ # ezllm 发布包设计文档
2
2
 
3
3
  > 本文档写发布包的**当前**结构与行为约束(包布局、路径 env 化、启停形态、更新链路、维护须知)。
4
4
  > 使用说明见 [README.md](../README.md)。
5
5
 
6
6
  ## 1. 目标与形态
7
7
 
8
- 把 monorepo(`app/cli` 发布包 / `app/web` 前端 / `packages/server` 服务 / `packages/gateway` 网关 / `packages/protocol` 协议层)里的 server 单进程应用,构建为可发布的 npm 包 **`ellm-proxy`**:
8
+ 把 monorepo(`app/cli` 发布包 / `app/web` 前端 / `packages/server` 服务 / `packages/gateway` 网关 / `packages/protocol` 协议层)里的 server 单进程应用,构建为可发布的 npm 包 **`ezllm`**:
9
9
 
10
10
  ```bash
11
- npm i -g ellm-proxy
12
- ellm-proxy start # 后台启动(隐藏窗口,首次自动创建配置)
13
- ellm-proxy stop # 停止后台服务
11
+ npm i -g ezllm
12
+ ezllm start # 后台启动(隐藏窗口,首次自动创建配置)
13
+ ezllm stop # 停止后台服务
14
14
  ```
15
15
 
16
16
  发布形态是**完整单进程应用**(网关 `/v1/*` + 管理台 `/api/*` + 管理域 API + web-ui 静态托管),不是 SDK。包名与内部子包 `gateway` 的相似性已知并接受。
@@ -50,7 +50,7 @@ app/cli/
50
50
  服务端不加载任何 `.env` / env 文件;环境变量一律由用户会话导出或 CLI 注入。约束与边界:
51
51
 
52
52
  - **server 源码零模式分支**:`paths.ts` 只有「env ?? 公式」两种取值,不存在模式判断;仓库开发模式不设任何 env 即工作。
53
- - **包模式必须经 CLI 启动**:绕过它裸跑 `dist/server.js` 时 env 未注入,会因找不到配置退出(报错信息指引)。官方入口始终是 `ellm-proxy start/stop`。
53
+ - **包模式必须经 CLI 启动**:绕过它裸跑 `dist/server.js` 时 env 未注入,会因找不到配置退出(报错信息指引)。官方入口始终是 `ezllm start/stop`。
54
54
  - **CLI 在转调前把路径参数统一转为绝对路径**:CLI 的 cwd 与 node 进程的工作目录不同,透传相对字符串会在另一基准下解析错位。
55
55
  - **CLI 负责首次配置自愈**(server 端只读不建):`ensureConfig()` 查找链与 server 端一致,创建后提示编辑或经管理台修改。
56
56
 
@@ -59,7 +59,7 @@ app/cli/
59
59
  - **start**:配置自愈 → 已运行检测(run.json + pid 存活)→ 端口预算(`--port` > `ELLM_PORT` > 配置 `port`;被占则顺延到空闲端口并提示,顺延仅本次运行生效)→ `spawnDetached` → 轮询 run.json + HTTP 探活(端口真实可服务才算就绪)→ 首启自启自愈。
60
60
  - **stop**:按 run.json 的 pid 杀进程树并清记录。`restart` = stop + start(保证最新参数生效)。
61
61
  - **进程管理原语**(`packages/server/src/proc/`):`run.json` 运行记录(pid/端口/startedAt/serverJs);`spawnDetached`(日志重定向到 `<appDir>/logs/`);`selfRespawn`(孵化后继进程后自身退出,`ELLM_RESPAWNED=1` 的后继对 EADDRINUSE 重试绑定而非顺延端口);`killTree`(Windows `taskkill /T /F`,POSIX 杀进程组);`waitHttpOnline`;`installCrashRecovery`(uncaughtException → respawn,1 分钟 10 次封顶)。
62
- - **开机自启**(`ellm-proxy autostart on|off`,跨平台、登录触发、免管理员):Windows 写启动文件夹(`.vbs` 隐藏窗口入口 + appDir 内 `.cmd`,`chcp 65001` 统一日志编码)、macOS 写 LaunchAgent、Linux 写 systemd user unit;自启项内容含解释器路径与 `ELLM_*` env 快照,保证登录启动与手动启动同参。
62
+ - **开机自启**(`ezllm autostart on|off`,跨平台、登录触发、免管理员):Windows 写启动文件夹(`.vbs` 隐藏窗口入口 + appDir 内 `.cmd`,`chcp 65001` 统一日志编码)、macOS 写 LaunchAgent、Linux 写 systemd user unit;自启项内容含解释器路径与 `ELLM_*` env 快照,保证登录启动与手动启动同参。
63
63
  - **解释器解析**(`proc/interpreter.ts` + get-node):服务依赖 `node:sqlite`(≥ 22.13),CLI 自身 node 过旧时解析/下载 node 22 再 spawn。
64
64
  - **管理域不设生命周期入口**:启停统一走 CLI(管理台由服务本体托管,页面无法自举拉起已停止的服务);管理域只留状态查询与设置类接口,每条路由带登录鉴权。
65
65
 
@@ -68,7 +68,7 @@ app/cli/
68
68
  自动更新采用「启动时生效 + self-respawn 双跳」:
69
69
 
70
70
  1. 检查(管理台请求惰性触发 + 定时检查每小时轮询,两通道共享互斥与墙钟节流;失败 10 分钟后可重试)发现新版本 → 写 `pendingUpdate` 标记 → respawn;
71
- 2. 新进程启动引导读到标记 → `npm i -g ellm-proxy@<版本>`(此时服务未起,无文件句柄冲突)→ 清标记 → 再 respawn 一次换新代码;
71
+ 2. 新进程启动引导读到标记 → `npm i -g ezllm@<版本>`(此时服务未起,无文件句柄冲突)→ 清标记 → 再 respawn 一次换新代码;
72
72
  3. 安装失败:清标记、写 `lastApply` 错误,以旧版本继续启动。
73
73
 
74
74
  开关是配置文件顶层 `autoUpdate`(运行状态 `lastCheck`/`lastApply`/`pendingUpdate` 存 `<appDir>/updater.json`);源码运行(包根不在 `node_modules` 下)时更新域整体关闭,防 dev 会话误更新全局包;更新源可用 `ELLM_UPDATE_REGISTRY` 覆盖(默认 npm 官方)。停机窗口 = npm 安装时长。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ezllm",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Self-hosted LLM gateway with admin web UI (single process, built-in process management)",
5
5
  "keywords": [
6
6
  "llm",
@@ -1 +0,0 @@
1
- @charset "UTF-8";.provider-card[data-v-e96d8595]{min-width:0;cursor:grab;transition:box-shadow .2s,border-color .2s}.provider-card[data-v-e96d8595] .el-card__header{padding-bottom:10px}.provider-card[data-v-e96d8595] .el-card__body{padding-top:12px}.provider-card[data-v-e96d8595]:hover{border-color:#b1d2ff;box-shadow:0 4px 16px #3b82f61a}.provider-card.dragging[data-v-e96d8595]{cursor:grabbing}.card-header[data-v-e96d8595]{display:flex;justify-content:space-between;align-items:center;gap:12px}.card-title[data-v-e96d8595]{display:flex;align-items:center;gap:8px;min-width:0}.provider-name[data-v-e96d8595]{font-size:15px;font-weight:600;color:#303133;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-actions[data-v-e96d8595]{display:flex;gap:0;flex-shrink:0;cursor:auto}.card-meta[data-v-e96d8595]{display:flex;flex-direction:column;gap:6px;padding:0}.meta-row[data-v-e96d8595]{display:flex;align-items:center;gap:10px;font-size:13px;line-height:20px}.meta-label[data-v-e96d8595]{flex-shrink:0;width:60px;color:#909399}.meta-value[data-v-e96d8595]{flex:1;min-width:0;color:#606266;font-family:ui-monospace,SF Mono,Consolas,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.model-tags[data-v-e96d8595]{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}.model-tag[data-v-e96d8595]{display:inline-flex;align-items:center;max-width:100%;padding:3px 10px;font-size:12px;line-height:18px;color:#606266;background:#f5f7fa;border:1px solid transparent;border-radius:999px;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:color .15s,background .15s}.model-tag[data-v-e96d8595]:hover{color:#3b82f6;background:#eff6ff}.model-tag.ok[data-v-e96d8595]{color:#16a34a;background:#f0fdf4}.model-tag.error[data-v-e96d8595]{color:#dc2626;background:#fef2f2}.badge-mark[data-v-e96d8595]{margin-left:3px}.card-footer[data-v-e96d8595]{display:flex;justify-content:space-between;align-items:center;margin-top:14px;padding-top:12px;border-top:1px solid #f0f2f5}.model-count[data-v-e96d8595]{font-size:13px;color:#16a34a;font-weight:500;cursor:pointer;border-radius:4px}.model-count[data-v-e96d8595]:hover{text-decoration:underline}.drag-hint-icon[data-v-e96d8595]{flex-shrink:0;color:#c0c4cc;cursor:grab}.drag-hint-icon[data-v-e96d8595]:hover{color:#909399}.detail-body[data-v-08dfa117]{max-height:70vh;overflow-y:auto}.detail-head[data-v-08dfa117]{display:flex;align-items:center;gap:12px;margin-bottom:12px}.detail-name[data-v-08dfa117]{font-weight:600;font-size:14px}.detail-pre[data-v-08dfa117]{background:#f5f7fa;border:1px solid #e4e7ed;border-radius:6px;padding:12px;font-size:12px;line-height:1.6;color:#303133;max-height:300px;overflow:auto;margin:0;white-space:pre-wrap;word-break:break-all}.protocol-help .help-intro[data-v-2248b61a]{font-size:13px;color:#606266;margin-bottom:14px}.protocol-help .help-item[data-v-2248b61a]{margin-bottom:12px}.protocol-help .help-title[data-v-2248b61a]{font-weight:600;font-size:14px;color:#303133;margin-bottom:4px;display:flex;align-items:center;gap:8px}.protocol-help .help-item p[data-v-2248b61a]{font-size:13px;color:#606266;line-height:1.7;margin:0}.protocol-help .help-table-note[data-v-2248b61a]{margin-bottom:8px}.protocol-help .help-table[data-v-2248b61a]{width:100%}.model-select-wrap[data-v-cd73a0be]{width:400px;flex-shrink:0}.test-result-link[data-v-cd73a0be]{font-size:12px}.col-help[data-v-cd73a0be]{margin-right:4px}.col-help-icon[data-v-cd73a0be]{color:#c0c4cc;cursor:help;vertical-align:-2px}.col-help-icon[data-v-cd73a0be]:hover{color:#909399}.dialog-body[data-v-05bf13ed]{max-height:80vh;overflow-y:auto;overflow-x:hidden;padding-bottom:100px}.test-ok-text[data-v-05bf13ed]{font-size:12px;color:#16a34a;max-width:140px;display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}.test-fail-text[data-v-05bf13ed]{font-size:12px;color:#dc2626;max-width:140px;display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}.model-select-wrap[data-v-05bf13ed]{width:400px;flex-shrink:0}.protocol-hint[data-v-05bf13ed]{line-height:1.8}.label-action-btn[data-v-05bf13ed]{margin-left:8px;font-size:12px;height:auto}.label-inline-toggle[data-v-05bf13ed]{margin-left:8px;display:inline-flex;align-items:center;gap:6px}.test-result-link[data-v-05bf13ed]{font-size:12px}.col-help[data-v-05bf13ed]{margin-right:4px}.col-help-icon[data-v-05bf13ed]{color:#c0c4cc;cursor:help;vertical-align:-2px}.col-help-icon[data-v-05bf13ed]:hover{color:#909399}.models-dialog-body[data-v-a79f8c7b]{display:flex;flex-direction:column;height:calc(94vh - 100px)}.models-dialog-body .models-table[data-v-a79f8c7b]{flex:1;min-height:0}.error-tag[data-v-a79f8c7b]{max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ghost[data-v-f3ad9421]{opacity:.4}.provider-grid[data-v-f3ad9421]{display:grid;grid-template-columns:repeat(2,1fr);gap:16px}@media(max-width:992px){.provider-grid[data-v-f3ad9421]{grid-template-columns:1fr}}