ellm-proxy 0.0.8 → 0.0.10
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.
- package/dist/cli.js +20 -6
- package/dist/server.js +17 -22
- package/dist/web-ui/assets/DashboardView-BTtCBA4g.css +1 -0
- package/dist/web-ui/assets/DashboardView-BWPFis7P.js +1 -0
- package/dist/web-ui/assets/LayoutView-BkUJgLW2.js +1 -0
- package/dist/web-ui/assets/LoginView-C8E6nJw2.js +1 -0
- package/dist/web-ui/assets/{LogsView-2h2Y0XE8.js → LogsView-CtRfyOCZ.js} +1 -1
- package/dist/web-ui/assets/{ProvidersView-bE1Hv9D-.js → ProvidersView-B6fm2FPQ.js} +1 -1
- package/dist/web-ui/assets/SettingsView-BHuXlGS1.js +1 -0
- package/dist/web-ui/assets/SetupView-BkBbUyaW.css +1 -0
- package/dist/web-ui/assets/SetupView-Oau3EJgh.js +1 -0
- package/dist/web-ui/assets/{config-nvSp8hHp.js → config-CHaFDQC7.js} +1 -1
- package/dist/web-ui/assets/{index-sPrBBnf5.js → index-BmtcBnoW.js} +18 -18
- package/dist/web-ui/assets/{providers-di7L8jy_.js → providers-Aw7T7j0r.js} +1 -1
- package/dist/web-ui/assets/useManagerApi-PldAaNYo.js +1 -0
- package/dist/web-ui/assets/useToast-C9BTyaBD.js +1 -0
- package/dist/web-ui/index.html +1 -1
- package/package.json +1 -1
- package/dist/web-ui/assets/DashboardView-3c_i_3VF.js +0 -1
- package/dist/web-ui/assets/DashboardView-BHG4xIdN.css +0 -1
- package/dist/web-ui/assets/LayoutView-BhN1f8wo.js +0 -1
- package/dist/web-ui/assets/LoginView-J23vsMu2.js +0 -1
- package/dist/web-ui/assets/ProcessView-Bn8Ph7rh.css +0 -1
- package/dist/web-ui/assets/ProcessView-Bs2k6aAf.js +0 -1
- package/dist/web-ui/assets/SettingsView-BmLmC-_v.js +0 -1
- package/dist/web-ui/assets/SetupView-Cgj7ubNz.js +0 -1
- package/dist/web-ui/assets/SetupView-kPJXJ7dX.css +0 -1
- package/dist/web-ui/assets/useManagerApi-qPjxfB0X.js +0 -1
- package/dist/web-ui/assets/useToast-D76qyWZg.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{Z as w,r as u,m as i}from"./index-BmtcBnoW.js";import{u as C}from"./config-CHaFDQC7.js";const J=w("providers",()=>{const s=C(),c=u(!1),n=i(()=>s.providers),v=i(()=>s.loading),f=i(()=>n.value.length),m=i(()=>n.value.reduce((e,o)=>{var r;return e+(((r=o.models)==null?void 0:r.length)??0)},0)),t=u([]);function p(){try{const e=localStorage.getItem("provider_order");e&&(t.value=JSON.parse(e))}catch{}}function d(){try{localStorage.setItem("provider_order",JSON.stringify(t.value))}catch{}}const g=i(()=>{const e=t.value.map(r=>n.value.find(a=>a.name===r)).filter(r=>!!r);for(const r of n.value)e.find(a=>a.name===r.name)||e.push(r);const o=e.map(r=>r.name);return(o.length!==t.value.length||o.some((r,a)=>t.value[a]!==r))&&(t.value=o,d()),e});function S(e){t.value=e.map(o=>o.name),d()}function h(e){t.value=[e,...t.value.filter(o=>o!==e)],d()}async function y(){p(),await s.load()}async function l(e){return s.config.providers=e,await s.save()}async function P(e){const o=n.value.filter(r=>r.name!==e.name);return await l(o)}async function O(e){const o=n.value.map(r=>({...r,models:(r.models??[]).filter(a=>a.modelName!==e)}));return await l(o)}return{suppliers:n,loading:v,restarting:c,orderedProviders:g,providerCount:f,totalModelCount:m,loadProviders:y,saveProviders:l,deleteProvider:P,deleteModel:O,reorderProviders:S,moveProviderToTop:h}});export{J as u};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ab as h}from"./index-BmtcBnoW.js";var w;const T=((w=window.__ELLM_CONFIG__)==null?void 0:w.API_BASE)??"",f=3e4;let u=null;function E(){return u===null&&(u=localStorage.getItem("webui_token")),u}function m(){u=null,localStorage.removeItem("webui_token"),h.push("/login")}async function e(t,c,s,p=f){const i={"Content-Type":"application/json"},a=E();a&&(i.Authorization=`Bearer ${a}`);const n=new AbortController,r=setTimeout(()=>n.abort(),p);try{const o=await fetch(`${T}${c}`,{method:t,headers:i,body:s!==void 0?JSON.stringify(s):void 0,signal:n.signal});if(o.status===401)throw m(),new Error("未登录");const d=await o.text();let l;try{l=d?JSON.parse(d):{}}catch{throw new Error(`响应格式错误 (HTTP ${o.status})`)}if(!o.ok||l.ok===!1)throw new Error(l.error||`HTTP ${o.status}`);return l}catch(o){throw o.name==="AbortError"?new Error("请求超时"):o instanceof TypeError?new Error("服务不可达(未启动或更新重启窗口期,请稍后重试)"):o}finally{clearTimeout(r)}}const A={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 S(t){const{targetVersion:c,sinceAppliedAt:s,timeoutMs:p=600*1e3,pollMs:i=3e3}=t,a=Date.now()+p;for(;;){if(Date.now()>=a)return null;await new Promise(o=>setTimeout(o,i));let n=null;try{n=await A.update()}catch{continue}const r=n.lastApply;if(r&&r.toVersion===c&&(r.appliedAt??0)>(s??0))return r;if(Date.now()>=a)return null}}export{A as m,S as w};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ac as a}from"./index-BmtcBnoW.js";function u(){function s(t,o="success"){a({message:t,type:o,duration:3e3})}return{toast:s}}export{u};
|
package/dist/web-ui/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>ELLM - 网关管理台</title>
|
|
8
8
|
<script src="/config.js"></script>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-BmtcBnoW.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="/assets/index-BecfPmag.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as H,p as J,e as h,g as o,w as s,c as V,i as B,r as y,A as T,a as _,j as Q,o as c,b as W,k as Y,t as u,f as l,h as p,F as X,q as Z,l as N,_ as ee}from"./index-sPrBBnf5.js";import{u as te}from"./providers-di7L8jy_.js";import{u as ae}from"./useToast-D76qyWZg.js";import"./config-nvSp8hHp.js";const le={class:"flex-col-gap"},oe=["onClick"],se={key:1},ne={class:"flex-between"},de={class:"flex-gap-8"},ue={class:"usage-grid"},ie={class:"usage-cell"},re={class:"usage-num orange"},ce={class:"usage-cell"},ve={class:"usage-num blue"},_e={class:"usage-cell"},pe={class:"usage-num green"},ge={class:"usage-cell"},me={class:"usage-num purple"},fe={class:"usage-cell"},he={class:"usage-num purple"},ye={key:0,class:"usage-cell"},be={class:"usage-num gray"},we={key:0},ke=["onClick"],De=H({__name:"DashboardView",setup(Ce){const k=te(),{toast:M}=ae(),i=y(null),b=y(!0),D=y(""),v=y(null),w=y("today"),C=a=>{const e=a.getFullYear(),t=String(a.getMonth()+1).padStart(2,"0"),r=String(a.getDate()).padStart(2,"0");return`${e}-${t}-${r}`},F=a=>{const e=new Date;let t,r=e;if(a==="today")t=e;else if(a==="yesterday")t=new Date(e),t.setDate(e.getDate()-1),r=t;else if(a==="beforeYesterday")t=new Date(e),t.setDate(e.getDate()-2),r=t;else if(a==="last3days")t=new Date(e),t.setDate(e.getDate()-2);else if(a==="week"){t=new Date(e);const S=(e.getDay()+6)%7;t.setDate(e.getDate()-S)}else if(a==="month")t=new Date(e.getFullYear(),e.getMonth(),1);else return;w.value=a,v.value=[C(t),C(r)],f()},U=a=>{a?F(a):(w.value="",v.value=null,f())},A=()=>{w.value="",f()},I=N(()=>{var e;const a=new Set;for(const t of k.suppliers)for(const r of t.models)a.add(r.modelName);if((e=i.value)!=null&&e.models)for(const t of i.value.models)a.add(t.model);return[...a].sort()}),f=async()=>{var a;b.value=!0;try{const e=new URLSearchParams;D.value&&e.set("model",D.value),((a=v.value)==null?void 0:a.length)===2&&(e.set("startDate",v.value[0]),e.set("endDate",v.value[1]));const t=e.toString();i.value=await T.get(`/api/usage${t?`?${t}`:""}`)}catch(e){console.error(e)}finally{b.value=!1}},R=async()=>{var a;try{const[e]=await Promise.all([T.get("/api/usage"),k.loadProviders(),T.get("/api/status").then(t=>x.value=t.listenPort).catch(()=>x.value)]);if(i.value=e,!((a=v.value)!=null&&a.length)){const t=new Date;v.value=[C(t),C(t)],await f()}}catch(e){console.error(e)}finally{b.value=!1}},L=window.location.hostname,x=y(Number(window.location.port)||null),q=N(()=>`http://${L}${x.value?`:${x.value}`:""}`),g=a=>a.toLocaleString(),E=a=>a==null||a<=0?"-":a>=1e3?(a/1e3).toFixed(2)+"s":Math.round(a)+"ms",$=async a=>{try{await navigator.clipboard.writeText(a),M("已复制到剪贴板","success")}catch{const e=document.createElement("textarea");e.value=a,e.style.position="fixed",e.style.opacity="0",document.body.appendChild(e),e.select();try{document.execCommand("copy"),M("已复制到剪贴板","success")}catch{M("复制失败","error")}document.body.removeChild(e)}};return J(R),(a,e)=>{const t=_("el-table-column"),r=_("el-table"),S=_("el-card"),m=_("el-option"),P=_("el-select"),j=_("el-date-picker"),z=_("el-button"),K=_("el-tag"),O=_("el-empty"),G=Q("loading");return c(),h("div",le,[o(S,{shadow:"never"},{header:s(()=>[...e[3]||(e[3]=[l("span",{class:"fw-600"},"服务信息",-1)])]),default:s(()=>[W((c(),V(r,{border:"",data:[{key:"API 地址",value:q.value},{key:"供应商数量",value:String(Y(k).providerCount)},{key:"模型总数",value:String(Y(k).totalModelCount)}],"show-header":!1,class:"full-width"},{default:s(()=>[o(t,{prop:"key",width:"160"}),o(t,null,{default:s(({row:d})=>[d.key==="API 地址"?(c(),h("span",{key:0,class:"copy-text",onClick:n=>$(d.value)},u(d.value),9,oe)):(c(),h("span",se,u(d.value),1))]),_:1})]),_:1},8,["data"])),[[G,b.value]])]),_:1}),i.value?(c(),V(S,{key:0,shadow:"never"},{header:s(()=>[l("div",ne,[e[5]||(e[5]=l("span",{class:"fw-600"},"用量统计",-1)),l("div",de,[o(P,{modelValue:D.value,"onUpdate:modelValue":e[0]||(e[0]=d=>D.value=d),placeholder:"全部模型",clearable:"",filterable:"",style:{width:"220px"},onChange:f},{default:s(()=>[(c(!0),h(X,null,Z(I.value,d=>(c(),V(m,{key:d,label:d,value:d},null,8,["label","value"]))),128))]),_:1},8,["modelValue"]),o(P,{modelValue:w.value,"onUpdate:modelValue":e[1]||(e[1]=d=>w.value=d),placeholder:"时间范围",clearable:"",style:{width:"130px"},onChange:U},{default:s(()=>[o(m,{label:"今天",value:"today"}),o(m,{label:"昨天",value:"yesterday"}),o(m,{label:"前天",value:"beforeYesterday"}),o(m,{label:"近三天",value:"last3days"}),o(m,{label:"本周",value:"week"}),o(m,{label:"本月",value:"month"})]),_:1},8,["modelValue"]),o(j,{modelValue:v.value,"onUpdate:modelValue":e[2]||(e[2]=d=>v.value=d),type:"daterange","range-separator":"至","start-placeholder":"开始日期","end-placeholder":"结束日期","value-format":"YYYY-MM-DD",style:{width:"260px"},onChange:A},null,8,["modelValue"]),o(z,{loading:b.value,onClick:f},{default:s(()=>[...e[4]||(e[4]=[p("刷新",-1)])]),_:1},8,["loading"])]),o(K,{type:"info"},{default:s(()=>[p(u(i.value.total.calls)+" 次调用",1)]),_:1})])]),default:s(()=>{var d;return[l("div",ue,[l("div",ie,[l("div",re,u(g(i.value.total.total_tokens)),1),e[6]||(e[6]=l("div",{class:"usage-label"},"总 Token",-1))]),l("div",ce,[l("div",ve,u(g(i.value.total.input_tokens)),1),e[7]||(e[7]=l("div",{class:"usage-label"},"输入 Token",-1))]),l("div",_e,[l("div",pe,u(g(i.value.total.output_tokens)),1),e[8]||(e[8]=l("div",{class:"usage-label"},"输出 Token",-1))]),l("div",ge,[l("div",me,u(g(i.value.total.cache_read_tokens)),1),e[9]||(e[9]=l("div",{class:"usage-label"},"缓存读取",-1))]),l("div",fe,[l("div",he,u(i.value.total.cache_rate!=null?i.value.total.cache_rate+"%":"-"),1),e[10]||(e[10]=l("div",{class:"usage-label"},"缓存命中率",-1))]),i.value.logStorageMB!=null?(c(),h("div",ye,[l("div",be,u(i.value.logStorageMB),1),e[11]||(e[11]=l("div",{class:"usage-label"},"日志占用 (MB)",-1))])):B("",!0)]),(d=i.value.models)!=null&&d.length?(c(),h("div",we,[e[12]||(e[12]=l("div",{class:"section-title"},"模型用量",-1)),o(r,{data:i.value.models,border:"",stripe:"",class:"full-width"},{default:s(()=>[o(t,{prop:"model",label:"模型","min-width":"200"},{default:s(({row:n})=>[l("span",{class:"copy-text",onClick:xe=>$(n.model)},u(n.model),9,ke)]),_:1}),o(t,{label:"调用次数",width:"90",align:"center"},{default:s(({row:n})=>[p(u(n.calls),1)]),_:1}),o(t,{label:"平均耗时",width:"110",align:"center"},{default:s(({row:n})=>[p(u(E(n.avg_duration)),1)]),_:1}),o(t,{label:"总 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.total_tokens)),1)]),_:1}),o(t,{label:"输入 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.input_tokens)),1)]),_:1}),o(t,{label:"输出 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.output_tokens)),1)]),_:1}),o(t,{label:"缓存读取",width:"130",align:"center"},{default:s(({row:n})=>[p(u(g(n.cache_read_tokens)),1)]),_:1}),o(t,{label:"缓存命中率",width:"110",align:"center"},{default:s(({row:n})=>[p(u(n.cache_rate!=null?n.cache_rate+"%":"-"),1)]),_:1})]),_:1},8,["data"])])):(c(),V(O,{key:1,description:"暂无调用记录","image-size":60}))]}),_:1})):B("",!0)])}}}),$e=ee(De,[["__scopeId","data-v-de31ace5"]]);export{$e as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.copy-text[data-v-de31ace5]{color:#409eff;cursor:pointer}.copy-text[data-v-de31ace5]:hover{text-decoration:underline}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as E,u as N,c as b,w as e,r as w,a,o as f,b as W,e as D,F as z,f as p,g as o,h as i,t as x,i as C,E as F,j as I,_ as L,k as S,l as R,m as j,n as O}from"./index-sPrBBnf5.js";import{m as A,w as q}from"./useManagerApi-qPjxfB0X.js";import{u as G}from"./useToast-D76qyWZg.js";const H={class:"check-body"},J={class:"version-row"},K={class:"version-row"},P=E({__name:"UpdateCheckDialog",props:{modelValue:{type:Boolean,default:!1},modelModifiers:{}},emits:["update:modelValue"],setup(B){const{toast:V}=G(),y=N(B,"modelValue"),m=w(!1),v=w(!1),n=w(null),g=w(null),u=w(null),r=async()=>{n.value=null,g.value=null,u.value=null,m.value=!0;try{n.value=await A.checkUpdate()}catch(d){g.value=d.message||"检查更新失败"}finally{m.value=!1}},t=async()=>{var d;if(n.value){try{await F.confirm(`确定更新到 ${n.value.latestVersion}?服务将自动重启并在启动时安装新版本(期间界面请求会短暂失败,属正常现象)。`,"更新确认",{confirmButtonText:"立即更新",cancelButtonText:"取消",type:"warning"})}catch{return}v.value=!0;try{const l=await A.update(),c=await A.applyUpdate();if(!c.updated){u.value=`已是最新版本 ${c.toVersion}`,n.value={hasUpdate:!1,currentVersion:c.toVersion,latestVersion:c.toVersion,checkedAt:Date.now()};return}u.value=`正在重启服务并安装 ${c.toVersion},请稍候(预计 1~3 分钟)...`;const s=await q({targetVersion:c.toVersion,sinceAppliedAt:((d=l.lastApply)==null?void 0:d.appliedAt)??null});s&&!s.error?(V(`已更新到 ${s.toVersion}`,"success"),u.value=`已更新 ${s.fromVersion} → ${s.toVersion}`,n.value={hasUpdate:!1,currentVersion:s.toVersion,latestVersion:s.toVersion,checkedAt:Date.now()}):s!=null&&s.error?(V(`更新失败:${s.error}`,"error"),u.value=`更新失败:${s.error}(服务已恢复旧版本)`):u.value="更新耗时较长,请稍后刷新页面查看结果"}catch(l){V(l.message||"更新失败","error")}finally{v.value=!1}}};return(d,l)=>{const c=a("el-tag"),s=a("el-alert"),k=a("el-button"),h=a("el-empty"),$=a("el-dialog"),U=I("loading");return f(),b($,{modelValue:y.value,"onUpdate:modelValue":l[2]||(l[2]=_=>y.value=_),title:"检查更新",width:"420px","close-on-click-modal":!v.value,onOpen:r},{footer:e(()=>{var _;return[(_=n.value)!=null&&_.hasUpdate?(f(),b(k,{key:0,type:"primary",loading:v.value,onClick:t},{default:e(()=>[i(x(v.value?"更新中...":"立即更新"),1)]),_:1},8,["loading"])):C("",!0),o(k,{onClick:l[1]||(l[1]=T=>y.value=!1)},{default:e(()=>[...l[6]||(l[6]=[i("关闭",-1)])]),_:1})]}),default:e(()=>[W((f(),D("div",H,[n.value?(f(),D(z,{key:0},[p("div",J,[l[3]||(l[3]=p("span",{class:"version-label"},"当前版本",-1)),o(c,{size:"small"},{default:e(()=>[i(x(n.value.currentVersion),1)]),_:1})]),p("div",K,[l[4]||(l[4]=p("span",{class:"version-label"},"最新版本",-1)),o(c,{type:n.value.hasUpdate?"warning":"success",size:"small","disable-transitions":""},{default:e(()=>[i(x(n.value.latestVersion),1)]),_:1},8,["type"])]),o(s,{title:n.value.hasUpdate?`发现新版本 ${n.value.latestVersion}`:"已是最新版本",type:n.value.hasUpdate?"warning":"success","show-icon":"",closable:!1,class:"mt-12"},null,8,["title","type"])],64)):!m.value&&g.value?(f(),b(h,{key:1,description:g.value},{default:e(()=>[o(k,{type:"primary",onClick:r},{default:e(()=>[...l[5]||(l[5]=[i("重试",-1)])]),_:1})]),_:1},8,["description"])):C("",!0)])),[[U,m.value]]),u.value?(f(),b(s,{key:0,title:u.value,type:"info","show-icon":"",class:"mt-12",onClose:l[0]||(l[0]=_=>u.value=null)},null,8,["title"])):C("",!0)]),_:1},8,["modelValue","close-on-click-modal"])}}}),Q=L(P,[["__scopeId","data-v-eaed444e"]]),X={class:"layout"},Y={class:"header-nav"},Z={class:"header-actions"},ee={class:"main"},te={class:"page"},ne=E({__name:"LayoutView",setup(B){const V=O(),y=j(),m=w(!1),v=(()=>{const r=localStorage.getItem("webui_token")||"";try{return atob(r).split(":")[0]||"用户"}catch{return"用户"}})(),n=R(()=>{const r=V.path;return r==="/"?"/":r.startsWith("/providers")?"/providers":r.startsWith("/settings")?"/settings":r.startsWith("/logs")?"/logs":r.startsWith("/process")?"/process":"/"}),g=()=>{localStorage.removeItem("webui_token"),y.push("/login")},u=r=>{r==="logout"&&g(),r==="check-update"&&(m.value=!0)};return(r,t)=>{const d=a("el-menu-item"),l=a("el-menu"),c=a("ArrowDown"),s=a("el-icon"),k=a("el-button"),h=a("el-dropdown-item"),$=a("el-dropdown-menu"),U=a("el-dropdown"),_=a("el-header"),T=a("router-view");return f(),D("div",X,[o(_,{class:"header"},{default:e(()=>[t[8]||(t[8]=p("h1",null,[p("span",null,"E"),i("LLM")],-1)),p("nav",Y,[o(l,{mode:"horizontal","default-active":n.value,ellipsis:!1,router:"",class:"header-menu"},{default:e(()=>[o(d,{index:"/"},{default:e(()=>[...t[1]||(t[1]=[i("首页",-1)])]),_:1}),o(d,{index:"/providers"},{default:e(()=>[...t[2]||(t[2]=[i("供应商管理",-1)])]),_:1}),o(d,{index:"/logs"},{default:e(()=>[...t[3]||(t[3]=[i("日志",-1)])]),_:1}),o(d,{index:"/settings"},{default:e(()=>[...t[4]||(t[4]=[i("设置",-1)])]),_:1}),o(d,{index:"/process"},{default:e(()=>[...t[5]||(t[5]=[i("进程管理",-1)])]),_:1})]),_:1},8,["default-active"])]),p("div",Z,[o(U,{trigger:"click",onCommand:u,class:"ml-12"},{dropdown:e(()=>[o($,null,{default:e(()=>[o(h,{command:"check-update"},{default:e(()=>[...t[6]||(t[6]=[i("检查更新",-1)])]),_:1}),o(h,{command:"logout",divided:""},{default:e(()=>[...t[7]||(t[7]=[i("退出",-1)])]),_:1})]),_:1})]),default:e(()=>[o(k,null,{default:e(()=>[i(x(S(v))+" ",1),o(s,{class:"el-icon--right"},{default:e(()=>[o(c)]),_:1})]),_:1})]),_:1})])]),_:1}),p("main",ee,[p("div",te,[o(T)])]),o(Q,{modelValue:m.value,"onUpdate:modelValue":t[0]||(t[0]=M=>m.value=M)},null,8,["modelValue"])])}}});export{ne as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as h,e as b,g as o,w as l,a,o as _,f as k,Z as x,a3 as f,c as C,i as L,h as B,r as i,A as N,m as I,_ as K}from"./index-sPrBBnf5.js";const A={class:"login-wrap"},E=h({__name:"LoginView",setup(M){const v=I(),n=i(""),s=i(""),t=i(""),d=i(!1);async function r(){if(!n.value){t.value="请输入用户名";return}if(!s.value){t.value="请输入密码";return}d.value=!0,t.value="";try{const u=await N.post("/api/auth/login",{username:n.value.trim(),password:s.value});localStorage.setItem("webui_token",u.token),v.push("/")}catch(u){t.value=u.message||"登录失败"}finally{d.value=!1}}return(u,e)=>{const p=a("el-input"),m=a("el-form-item"),w=a("el-alert"),g=a("el-button"),V=a("el-form"),y=a("el-card");return _(),b("div",A,[o(y,{shadow:"hover",class:"login-card"},{default:l(()=>[e[3]||(e[3]=k("h2",{class:"login-title"},"ELLM",-1)),o(V,{"label-position":"top",onSubmit:x(r,["prevent"])},{default:l(()=>[o(m,{label:"用户名"},{default:l(()=>[o(p,{modelValue:n.value,"onUpdate:modelValue":e[0]||(e[0]=c=>n.value=c),placeholder:"请输入用户名",onKeyup:f(r,["enter"])},null,8,["modelValue"])]),_:1}),o(m,{label:"密码"},{default:l(()=>[o(p,{modelValue:s.value,"onUpdate:modelValue":e[1]||(e[1]=c=>s.value=c),type:"password",placeholder:"请输入密码","show-password":"",onKeyup:f(r,["enter"])},null,8,["modelValue"])]),_:1}),t.value?(_(),C(w,{key:0,title:t.value,type:"error","show-icon":"",class:"mb-16"},null,8,["title"])):L("",!0),o(g,{type:"primary",class:"full-width",loading:d.value,onClick:r},{default:l(()=>[...e[2]||(e[2]=[B(" 登录 ",-1)])]),_:1},8,["loading"])]),_:1})]),_:1})])}}}),U=K(E,[["__scopeId","data-v-c944978e"]]);export{U as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@charset "UTF-8";.proc-name[data-v-43da5cf5]{display:flex;align-items:center;gap:8px;font-weight:600}.proc-name .proc-sub[data-v-43da5cf5]{font-weight:400;color:var(--el-text-color-secondary);font-size:12px}.status-dot[data-v-43da5cf5]{display:inline-block;width:8px;height:8px;border-radius:50%}.status-dot.running[data-v-43da5cf5]{background:var(--el-color-success);box-shadow:0 0 4px var(--el-color-success)}.status-dot.stopped[data-v-43da5cf5]{background:var(--el-color-info)}.status-dot.abnormal[data-v-43da5cf5]{background:var(--el-color-danger)}.desc-cell[data-v-43da5cf5]{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.desc-full[data-v-43da5cf5]{margin:0;line-height:1.8}.mr-8[data-v-43da5cf5]{margin-right:8px}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as X,p as q,a8 as H,b as J,e as D,g as s,w as l,r as i,a as c,j as Q,o as w,f as d,$ as W,h as o,t as r,c as Y,E as Z,_ as ee}from"./index-sPrBBnf5.js";import{m as E}from"./useManagerApi-qPjxfB0X.js";import{u as te}from"./useToast-D76qyWZg.js";const ae={class:"flex-col-gap"},le={class:"flex-between"},se={class:"proc-name"},ne={class:"proc-sub"},oe=["onClick"],re={key:1,class:"muted-sm"},ie={class:"desc-full"},de=X({__name:"ProcessView",setup(ue){const{toast:b}=te(),v=i(!0),g=i(!1),y=i([]),x=i(null),p=i(null),h=i(Date.now()),I={main:"代理服务"},C=e=>I[e.role]??e.role,k={main:"代理服务(ellm-proxy):网关本体,转发 /v1/* 模型请求,同时承载管理台页面与全部管理 API;本页的重启操作即作用于该进程——服务会先孵化新进程接管端口再退出,期间网关与管理台短暂不可用。"},O=e=>k[e.role],_=i(!1),T=i(""),$=i(""),P=e=>{T.value=`${C(e)}(${M()})`,$.value=k[e.role],_.value=!0},M=e=>"ellm-proxy",A=e=>e.status==="online",N=e=>e.status?e.status==="online"?"running":e.status==="errored"?"abnormal":"stopped":"stopped",V=e=>{switch(e.status){case"online":return{text:"运行中",type:"success"};case"stopped":return{text:"已停止",type:"info"};case"errored":return{text:"异常",type:"danger"};case"waiting restart":case"launching":return{text:"重启中",type:"warning"};default:return{text:e.status||"未知",type:"info"}}},R=e=>{if(!A(e)||!e.startedAt)return"-";let t=Math.max(0,Math.floor((h.value-e.startedAt)/1e3));const f=Math.floor(t/86400);t-=f*86400;const u=Math.floor(t/3600);t-=u*3600;const n=Math.floor(t/60);return t-=n*60,[f?`${f}天`:"",u?`${u}时`:"",n?`${n}分`:"",`${t}秒`].filter(Boolean).join(" ")},S=e=>e?e>=1024**3?(e/1024**3).toFixed(1)+"G":e>=1024**2?(e/1024**2).toFixed(1)+"M":Math.round(e/1024)+"K":"-",m=async e=>{e||(y.value.length?g.value=!0:v.value=!0);try{y.value=await E.processes(),h.value=Date.now(),x.value=null}catch(t){e||(x.value=t.message)}finally{e||(v.value=!1,g.value=!1)}};let B;const j=()=>{p.value||document.hidden||m(!0)};q(()=>{m(!1),B=window.setInterval(j,5e3)}),H(()=>window.clearInterval(B));const z=async()=>{try{await Z.confirm("确定重启 ellm-proxy 代理服务?重启期间网关与管理台将短暂不可用(服务自动孵化新进程接管端口)。","重启确认",{confirmButtonText:"重启",cancelButtonText:"取消",type:"warning"})}catch{return}p.value="restart";try{await E.restart(),b("重启指令已发送,服务重启中","success")}catch(e){b(e.message||"操作失败","error")}finally{p.value=null,await m(!0)}};return(e,t)=>{const f=c("el-tag"),u=c("el-button"),n=c("el-table-column"),F=c("el-empty"),L=c("el-table"),U=c("el-card"),G=c("el-dialog"),K=Q("loading");return J((w(),D("div",ae,[s(U,{shadow:"never"},{header:l(()=>[d("div",le,[t[6]||(t[6]=d("span",{class:"fw-600"},"进程管理",-1)),d("div",null,[s(f,{type:"info",class:"mr-8"},{default:l(()=>[...t[4]||(t[4]=[o("刷新间隔 5s",-1)])]),_:1}),s(u,{loading:g.value,onClick:t[0]||(t[0]=a=>m(!1))},{default:l(()=>[...t[5]||(t[5]=[o("刷新",-1)])]),_:1},8,["loading"])])])]),default:l(()=>[s(L,{data:y.value,border:"",stripe:"",class:"full-width"},{empty:l(()=>[s(F,{description:x.value||"暂无进程数据"},{default:l(()=>[s(u,{type:"primary",onClick:t[1]||(t[1]=a=>m(!1))},{default:l(()=>[...t[8]||(t[8]=[o("重试",-1)])]),_:1})]),_:1},8,["description"])]),default:l(()=>[s(n,{label:"进程","min-width":"180"},{default:l(({row:a})=>[d("div",se,[d("span",{class:W(["status-dot",N(a)])},null,2),o(" "+r(C(a))+" ",1),d("span",ne,r(M(a)),1)])]),_:1}),s(n,{label:"运行状态",width:"100",align:"center"},{default:l(({row:a})=>[s(f,{type:V(a).type,"disable-transitions":""},{default:l(()=>[o(r(V(a).text),1)]),_:2},1032,["type"])]),_:1}),s(n,{label:"PID",width:"90",align:"center"},{default:l(({row:a})=>[o(r(a.pid??"-"),1)]),_:1}),s(n,{label:"端口",width:"90",align:"center"},{default:l(({row:a})=>[o(r(a.port??"-"),1)]),_:1}),s(n,{label:"运行时长",width:"130",align:"center"},{default:l(({row:a})=>[o(r(R(a)),1)]),_:1}),s(n,{label:"地址","min-width":"170"},{default:l(({row:a})=>[o(r(a.address??"-"),1)]),_:1}),s(n,{label:"内存",width:"90",align:"center"},{default:l(({row:a})=>[o(r(S(a.memory)),1)]),_:1}),s(n,{label:"描述","min-width":"220"},{default:l(({row:a})=>[d("span",{class:"desc-cell",title:"点击查看详情",onClick:ce=>P(a)},r(O(a)),9,oe)]),_:1}),s(n,{label:"操作",width:"90",align:"center"},{default:l(({row:a})=>[a.role==="main"?(w(),Y(u,{key:0,type:"primary",size:"small",loading:p.value==="restart",disabled:p.value!==null,onClick:z},{default:l(()=>[...t[7]||(t[7]=[o(" 重启 ",-1)])]),_:1},8,["loading","disabled"])):(w(),D("span",re,"不可操作"))]),_:1})]),_:1},8,["data"]),t[9]||(t[9]=d("div",{class:"muted-sm"}," 服务为单进程架构(网关、管理台 API 与界面同进程同端口):重启期间网关与管理台短暂不可用, 服务会自动孵化新进程接管端口,无需手动拉起。 ",-1))]),_:1}),s(G,{modelValue:_.value,"onUpdate:modelValue":t[3]||(t[3]=a=>_.value=a),title:T.value,width:"520px"},{footer:l(()=>[s(u,{type:"primary",onClick:t[2]||(t[2]=a=>_.value=!1)},{default:l(()=>[...t[10]||(t[10]=[o("知道了",-1)])]),_:1})]),default:l(()=>[d("p",ie,r($.value),1)]),_:1},8,["modelValue","title"])])),[[K,v.value]])}}}),_e=ee(de,[["__scopeId","data-v-43da5cf5"]]);export{_e as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{u as F}from"./useToast-D76qyWZg.js";import{d as Y,p as X,b as q,o as m,c as B,w as n,e as x,f as s,t as $,i as K,h as i,g as a,F as H,r,E as W,a as c,j as G,_ as Q,l as j,A as ee}from"./index-sPrBBnf5.js";import{u as le}from"./config-nvSp8hHp.js";import{m as I,w as te}from"./useManagerApi-qPjxfB0X.js";const se={class:"flex-between"},ae={class:"muted-sm mb-8"},oe={class:"switch-row"},ne={class:"switch-info"},ie={class:"switch-title"},ue={class:"switch-row"},de={class:"switch-info"},re={class:"switch-title"},ce={class:"switch-row"},ve={class:"switch-info"},pe={class:"switch-title"},me=Y({__name:"HarnessSwitches",setup(J){const{toast:w}=F(),k=r(!0),U=r(!1),v=r(null),A=r(null),d=r(null),V=r(null),y=r("success"),h=async _=>{_||(v.value?U.value=!0:k.value=!0);try{v.value=await I.harness(),A.value=null}catch(o){_||(A.value=o.message)}finally{_||(k.value=!1,U.value=!1)}};X(()=>h(!1));const E={claudeCode:{label:"Claude Code",file:"~/.claude/settings.json",toggle:I.setClaudeCode},codex:{label:"Codex CLI",file:"~/.codex/config.toml",toggle:I.setCodex},dsh:{label:"DeepSeek Harness",file:"~/.dsh/settings.yaml",toggle:I.setDsh}},g=async(_,o)=>{if(!v.value||d.value)return;const b=E[_],p=o?`确定开启 ${b.label} 代理?将改写 ${b.file}(开启前自动备份,关闭时从备份还原)。`:`确定关闭 ${b.label} 代理?将从备份还原 ${b.file}(无备份时移除代理相关配置)。`;try{await W.confirm(p,o?"开启确认":"关闭确认",{confirmButtonText:o?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}d.value=_;try{const l=await b.toggle(o);V.value=l.note??null,y.value=o?"success":"info",w(o?"已开启代理":"已关闭代理","success")}catch(l){w(l.message||"操作失败","error")}finally{d.value=null,await h(!0)}};return(_,o)=>{const b=c("el-button"),p=c("el-alert"),l=c("el-tag"),P=c("el-switch"),z=c("el-divider"),D=c("el-empty"),R=c("el-card"),N=G("loading");return q((m(),B(R,{shadow:"never"},{header:n(()=>[s("div",se,[o[7]||(o[7]=s("span",{class:"fw-600"},"代理开关",-1)),a(b,{loading:U.value,onClick:o[0]||(o[0]=T=>h(!1))},{default:n(()=>[...o[6]||(o[6]=[i("刷新",-1)])]),_:1},8,["loading"])])]),default:n(()=>[v.value?(m(),x(H,{key:0},[s("div",ae," 将本机 CLI 工具的流量一键指向 ellm-proxy 网关("+$(v.value.gatewayBase)+")。 开启前自动备份原配置为 *.ellm-bak(保留最早的原始备份),关闭时从备份还原。 ",1),V.value?(m(),B(p,{key:0,title:V.value,type:y.value,"show-icon":"",class:"mb-8",onClose:o[1]||(o[1]=T=>V.value=null)},null,8,["title","type"])):K("",!0),s("div",oe,[s("div",ne,[s("div",ie,[o[8]||(o[8]=i(" Claude Code ",-1)),a(l,{type:v.value.claudeCode?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i($(v.value.claudeCode?"已开启":"未开启"),1)]),_:1},8,["type"])]),o[9]||(o[9]=s("div",{class:"switch-desc"},[i(" 写入 "),s("code",null,"~/.claude/settings.json"),i(" 的 env.ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN, 鉴权取网关配置的 masterKey(未设置将开启失败);其余字段原样保留,重启 Claude Code 会话后生效。 ")],-1))]),a(P,{"model-value":v.value.claudeCode,loading:d.value==="claudeCode",onChange:o[2]||(o[2]=T=>g("claudeCode",!!T))},null,8,["model-value","loading"])]),a(z,{class:"switch-divider"}),s("div",ue,[s("div",de,[s("div",re,[o[10]||(o[10]=i(" Codex CLI ",-1)),a(l,{type:v.value.codex?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i($(v.value.codex?"已开启":"未开启"),1)]),_:1},8,["type"])]),o[11]||(o[11]=s("div",{class:"switch-desc"},[i(" 写入 "),s("code",null,"~/.codex/config.toml"),i(" 的 model_provider 与 [model_providers.ellm-proxy] 段 (保留用户注释与排版);开启后需按提示设置环境变量 ELLM_PROXY_API_KEY 供 Codex 鉴权。 ")],-1))]),a(P,{"model-value":v.value.codex,loading:d.value==="codex",onChange:o[3]||(o[3]=T=>g("codex",!!T))},null,8,["model-value","loading"])]),a(z,{class:"switch-divider"}),s("div",ce,[s("div",ve,[s("div",pe,[o[12]||(o[12]=i(" DeepSeek Harness ",-1)),a(l,{type:v.value.dsh?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i($(v.value.dsh?"已开启":"未开启"),1)]),_:1},8,["type"])]),o[13]||(o[13]=s("div",{class:"switch-desc"},[i(" 写入 "),s("code",null,"~/.dsh/settings.yaml"),i(" 的 llm-deepseek.baseURL 与 "),s("code",null,"~/.dsh/.credentials.yaml"),i(" 的 DEEPSEEK_API_KEY(取网关 masterKey,未设置则写占位值);模型请在 dsh 内选择与网关部署名一致的模型。 dsh 为 developer preview,配置格式可能随版本变化。 ")],-1))]),a(P,{"model-value":v.value.dsh,loading:d.value==="dsh",onChange:o[4]||(o[4]=T=>g("dsh",!!T))},null,8,["model-value","loading"])])],64)):(m(),B(D,{key:1,description:A.value||"暂无开关状态"},{default:n(()=>[a(b,{type:"primary",onClick:o[5]||(o[5]=T=>h(!1))},{default:n(()=>[...o[14]||(o[14]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1})),[[N,k.value]])}}}),fe=Q(me,[["__scopeId","data-v-65e65156"]]),_e={class:"service-settings"},ge={class:"flex-between"},ye={key:0,class:"switch-row"},we={class:"switch-info"},be={class:"switch-title"},Ce={key:0,class:"switch-row"},ke={class:"switch-info"},he={class:"switch-title"},$e={class:"switch-desc"},Ve={key:0,class:"version-block"},xe={class:"version-row"},Ae={class:"muted-sm"},Te={key:0,class:"muted-sm"},Ue={class:"version-actions"},Ee=Y({__name:"ServiceUpdateSettings",setup(J){const{toast:w}=F(),k=r(!0),U=r(!1),v=r(null),A=r(null),d=r(null),V=r(null),y=r(null),h=r(!1),E=r(!1),g=r(null),_=async t=>{var e,u;if(!t){if(U.value)return;U.value=!0}try{const[f,S]=await Promise.allSettled([I.autostart(),I.update()]);v.value=f.status==="fulfilled"?f.value:null,A.value=f.status==="rejected"?(e=f.reason)==null?void 0:e.message:null,d.value=S.status==="fulfilled"?S.value:null,V.value=S.status==="rejected"?(u=S.reason)==null?void 0:u.message:null}finally{k.value=!1,U.value=!1}};X(()=>_(!1));const o=j(()=>{var t,e;return((e=(t=d.value)==null?void 0:t.lastCheck)==null?void 0:e.currentVersion)??null}),b=j(()=>{var t,e;return((e=(t=d.value)==null?void 0:t.lastCheck)==null?void 0:e.latestVersion)??null}),p=j(()=>{var t,e;return((e=(t=d.value)==null?void 0:t.lastCheck)==null?void 0:e.hasUpdate)===!0}),l=j(()=>{var e,u;const t=(u=(e=d.value)==null?void 0:e.lastCheck)==null?void 0:u.checkedAt;return t?new Date(t).toLocaleString():null}),P=j(()=>{var u;const t=(u=d.value)==null?void 0:u.lastApply;if(!t)return null;const e=t.appliedAt?new Date(t.appliedAt).toLocaleString():"";return`${t.fromVersion} → ${t.toVersion}${e?`(${e})`:""}`}),z=t=>t?new Date(t).toLocaleString():"",D=async t=>{if(!v.value||y.value)return;const e=t?"确定开启开机自启?将注册登录触发的自启项(登录时自动恢复服务)。":"确定关闭开机自启?将移除自启项,且此后首次启动不再自动注册。";try{await W.confirm(e,t?"开启确认":"关闭确认",{confirmButtonText:t?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}y.value="autostart";try{const u=await I.setAutostart(t);u.note?w(u.note,"success"):w(t?"开机自启已开启":"开机自启已关闭","success")}catch(u){w(u.message||"操作失败","error")}finally{y.value=null,await _(!0)}},R=async t=>{if(!d.value||y.value)return;const e=t?"确定开启自动更新?将每小时检查私有源,发现新版本自动安装并重启服务。":"确定关闭自动更新?不再自动检查(手动检查更新不受影响)。";try{await W.confirm(e,t?"开启确认":"关闭确认",{confirmButtonText:t?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}y.value="updateEnabled";try{const u=await I.setUpdateEnabled(t);u.note&&w(u.note,"success")}catch(u){w(u.message||"操作失败","error")}finally{y.value=null,await _(!0)}},N=async()=>{h.value=!0;try{const t=await I.checkUpdate();g.value=t.hasUpdate?`发现新版本 ${t.latestVersion}(当前 ${t.currentVersion}),可点击「立即更新」安装`:`已是最新版本 ${t.currentVersion}(检查于 ${z(t.checkedAt)})`,await _(!0)}catch(t){w(t.message||"检查更新失败","error")}finally{h.value=!1}},T=async()=>{var t;try{await W.confirm(`确定更新到 ${b.value}?服务将自动重启并在启动时安装新版本(期间界面请求会短暂失败,属正常现象)。`,"更新确认",{confirmButtonText:"立即更新",cancelButtonText:"取消",type:"warning"})}catch{return}E.value=!0;try{const e=await I.update(),u=await I.applyUpdate();if(!u.updated){g.value=`已是最新版本 ${u.toVersion}`;return}g.value=`正在重启服务并安装 ${u.toVersion},请稍候(预计 1~3 分钟)...`;const f=await te({targetVersion:u.toVersion,sinceAppliedAt:((t=e.lastApply)==null?void 0:t.appliedAt)??null});f&&!f.error?(w(`已更新到 ${f.toVersion}`,"success"),g.value=`已更新 ${f.fromVersion} → ${f.toVersion}`):f!=null&&f.error?(w(`更新失败:${f.error}`,"error"),g.value=`更新失败:${f.error}(服务已恢复旧版本)`):g.value="更新耗时较长,请稍后刷新页面查看结果"}catch(e){w(e.message||"更新失败","error")}finally{E.value=!1,await _(!0)}};return(t,e)=>{const u=c("el-button"),f=c("el-tag"),S=c("el-switch"),M=c("el-empty"),O=c("el-card"),C=c("el-alert"),Z=G("loading");return q((m(),x("div",_e,[a(O,{shadow:"never",class:"mb-16"},{header:n(()=>[s("div",ge,[e[7]||(e[7]=s("span",{class:"fw-600"},"开机自启",-1)),a(u,{loading:U.value,onClick:e[0]||(e[0]=L=>_(!1))},{default:n(()=>[...e[6]||(e[6]=[i("刷新",-1)])]),_:1},8,["loading"])])]),default:n(()=>[v.value?(m(),x("div",ye,[s("div",we,[s("div",be,[e[8]||(e[8]=i(" 登录时自动启动服务 ",-1)),a(f,{type:v.value.enabled?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i($(v.value.enabled?"已开启":"未开启"),1)]),_:1},8,["type"])]),e[9]||(e[9]=s("div",{class:"switch-desc"},[i(" 登录操作系统时自动启动服务(无需管理员权限)。 关闭后首次启动 "),s("code",null,"ellm-proxy"),i(" 也不会再自动注册,重新开启即可解除。 ")],-1))]),a(S,{"model-value":v.value.enabled,loading:y.value==="autostart",onChange:e[1]||(e[1]=L=>D(!!L))},null,8,["model-value","loading"])])):(m(),B(M,{key:1,description:A.value||"暂无自启状态"},{default:n(()=>[a(u,{type:"primary",onClick:e[2]||(e[2]=L=>_(!1))},{default:n(()=>[...e[10]||(e[10]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1}),a(O,{shadow:"never",class:"mb-16"},{header:n(()=>[...e[11]||(e[11]=[s("div",{class:"flex-between"},[s("span",{class:"fw-600"},"自动更新")],-1)])]),default:n(()=>[d.value?(m(),x("div",Ce,[s("div",ke,[s("div",he,[e[12]||(e[12]=i(" 自动检查并安装新版本 ",-1)),a(f,{type:d.value.enabled?"success":"info",size:"small","disable-transitions":""},{default:n(()=>[i($(d.value.enabled?"已开启":"未开启"),1)]),_:1},8,["type"])]),s("div",$e,[d.value.supported?(m(),x(H,{key:1},[i(" 每小时检查 npm 源,发现新版本自动重启服务并在启动时安装(更新期间服务短暂中断); 关闭后仍可在下方手动检查与更新。 ")],64)):(m(),x(H,{key:0},[i(" 当前为源码运行模式,更新功能不可用(仅 npm 全局安装的包支持自动更新)。 ")],64))])]),d.value.supported?(m(),B(S,{key:0,"model-value":d.value.enabled,loading:y.value==="updateEnabled",onChange:e[3]||(e[3]=L=>R(!!L))},null,8,["model-value","loading"])):K("",!0)])):(m(),B(M,{key:1,description:V.value||"暂无更新状态"},{default:n(()=>[a(u,{type:"primary",onClick:e[4]||(e[4]=L=>_(!1))},{default:n(()=>[...e[13]||(e[13]=[i("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1}),a(O,{shadow:"never"},{header:n(()=>[...e[14]||(e[14]=[s("div",{class:"flex-between"},[s("span",{class:"fw-600"},"版本信息")],-1)])]),default:n(()=>[d.value?(m(),x("div",Ve,[s("div",xe,[e[17]||(e[17]=s("span",{class:"version-label"},"当前版本",-1)),a(f,{size:"small"},{default:n(()=>[i($(o.value||"未知"),1)]),_:1}),b.value?(m(),x(H,{key:0},[e[16]||(e[16]=s("span",{class:"version-label"},"最新版本",-1)),a(f,{type:p.value?"warning":"success",size:"small","disable-transitions":""},{default:n(()=>[i($(b.value),1)]),_:1},8,["type"]),p.value?(m(),B(f,{key:0,type:"warning",size:"small","disable-transitions":""},{default:n(()=>[...e[15]||(e[15]=[i("发现新版本",-1)])]),_:1})):K("",!0)],64)):K("",!0)]),s("div",Ae,[i(" 上次检查:"+$(l.value||"尚未检查")+" ",1),d.value.updating?(m(),x(H,{key:0},[i(" · 更新执行中...")],64)):K("",!0)]),P.value?(m(),x("div",Te,"上次更新:"+$(P.value),1)):K("",!0),s("div",Ue,[a(u,{loading:h.value,disabled:d.value.updating,onClick:N},{default:n(()=>[i($(h.value?"检查中...":"检查更新"),1)]),_:1},8,["loading","disabled"]),p.value?(m(),B(u,{key:0,type:"primary",loading:E.value,disabled:d.value.updating,onClick:T},{default:n(()=>[i($(E.value?"更新中...":`立即更新到 ${b.value}`),1)]),_:1},8,["loading","disabled"])):K("",!0)]),g.value?(m(),B(C,{key:1,title:g.value,type:"info","show-icon":"",class:"mt-8",onClose:e[5]||(e[5]=L=>g.value=null)},null,8,["title"])):K("",!0)])):(m(),B(M,{key:1,description:"更新状态不可用"}))]),_:1})])),[[Z,k.value]])}}}),Se=Q(Ee,[["__scopeId","data-v-9366cd99"]]),Be={class:"flex-col-gap"},Ie={class:"flex-between"},Pe={class:"muted-sm"},De={key:0},Ke={class:"flex-end"},Re=Y({__name:"SettingsView",setup(J){const{toast:w}=F(),k=le(),U=r("basic"),v=r(!0),A=r(!1),d=r(""),V=r(29770),y=r(null),h=r("passthrough"),E=r(!1),g=r(""),_=r(""),o=async()=>{var p;try{await k.load(),d.value=k.config.masterKey||"",V.value=k.config.port??29770,h.value=k.config.compression==="plain"?"plain":"passthrough",E.value=k.config.logContent===!0;const l=(p=k.config["web-ui"])==null?void 0:p["login-account"];g.value=(l==null?void 0:l.username)||"",_.value=(l==null?void 0:l.password)||"";try{y.value=await ee.get("/api/status").then(P=>P.listenPort)}catch{y.value=null}}catch(l){w(l.message,"error")}finally{v.value=!1}},b=async()=>{A.value=!0;try{await k.update(p=>{var l;p.masterKey=d.value.trim(),p.port=V.value,p.compression=h.value,p.logContent=E.value,p["web-ui"]??(p["web-ui"]={}),(l=p["web-ui"])["login-account"]??(l["login-account"]={}),p["web-ui"]["login-account"].username=g.value.trim(),p["web-ui"]["login-account"].password=_.value}),w("设置已保存并生效","success")}catch(p){w(p.message,"error")}finally{A.value=!1}};return X(o),(p,l)=>{const P=c("el-tag"),z=c("el-input"),D=c("el-form-item"),R=c("el-input-number"),N=c("el-option"),T=c("el-select"),t=c("el-switch"),e=c("el-form"),u=c("el-card"),f=c("el-button"),S=c("el-tab-pane"),M=c("el-tabs"),O=G("loading");return m(),B(M,{modelValue:U.value,"onUpdate:modelValue":l[6]||(l[6]=C=>U.value=C),class:"settings-tabs"},{default:n(()=>[a(S,{label:"基本设置",name:"basic"},{default:n(()=>[q((m(),x("div",Be,[a(u,{shadow:"never"},{header:n(()=>[s("div",Ie,[l[8]||(l[8]=s("span",{class:"fw-600"},"基本设置",-1)),a(P,{type:"success"},{default:n(()=>[...l[7]||(l[7]=[i("运行中",-1)])]),_:1})])]),default:n(()=>[a(e,{"label-position":"top"},{default:n(()=>[a(D,{label:"Master Key"},{default:n(()=>[a(z,{modelValue:d.value,"onUpdate:modelValue":l[0]||(l[0]=C=>d.value=C),type:"password",placeholder:"sk-1234","show-password":""},null,8,["modelValue"]),l[9]||(l[9]=s("div",{class:"muted-sm"}," 网关 API 认证密钥(/v1/* 请求需携带 Bearer <masterKey>;留空 = 本地放行) ",-1))]),_:1}),a(D,{label:"监听端口 (port)"},{default:n(()=>[a(R,{modelValue:V.value,"onUpdate:modelValue":l[1]||(l[1]=C=>V.value=C),min:1,max:65535,step:1,class:"full-width"},null,8,["modelValue"]),s("div",Pe,[l[11]||(l[11]=i(" 网关与管理台 API 的监听端口,保存后下次重启生效 ",-1)),y.value!==null?(m(),x(H,{key:0},[l[10]||(l[10]=i(" ;当前实际监听 ",-1)),s("b",null,$(y.value),1),y.value!==V.value?(m(),x("span",De,"(由 PORT 环境变量覆盖,此配置值不生效)")):K("",!0)],64)):K("",!0)])]),_:1}),a(D,{label:"压缩语义 (compression)"},{default:n(()=>[a(T,{modelValue:h.value,"onUpdate:modelValue":l[2]||(l[2]=C=>h.value=C),class:"full-width"},{default:n(()=>[a(N,{label:"透传 (passthrough)",value:"passthrough"}),a(N,{label:"明文 (plain)",value:"plain"})]),_:1},8,["modelValue"]),l[12]||(l[12]=s("div",{class:"muted-sm"},"上游响应压缩处理方式,保存后热更新生效",-1))]),_:1}),a(D,{label:"日志内容捕获 (logContent)"},{default:n(()=>[a(t,{modelValue:E.value,"onUpdate:modelValue":l[3]||(l[3]=C=>E.value=C)},null,8,["modelValue"]),l[13]||(l[13]=s("div",{class:"muted-sm"}," 记录完整请求/响应(含上游原始与转换后内容)进日志详情,用于排查定位问题;开启后有少量内存与存储开销,保存后热更新生效 ",-1))]),_:1}),a(D,{label:"管理台用户名 (web-ui.login-account.username)"},{default:n(()=>[a(z,{modelValue:g.value,"onUpdate:modelValue":l[4]||(l[4]=C=>g.value=C),placeholder:"admin"},null,8,["modelValue"]),l[14]||(l[14]=s("div",{class:"muted-sm"}," 管理台登录用户名,留空回退 WEBUI_USERNAME 环境变量或默认 admin ",-1))]),_:1}),a(D,{label:"管理台密码 (web-ui.login-account.password)"},{default:n(()=>[a(z,{modelValue:_.value,"onUpdate:modelValue":l[5]||(l[5]=C=>_.value=C),type:"password",placeholder:"admin","show-password":""},null,8,["modelValue"]),l[15]||(l[15]=s("div",{class:"muted-sm"}," 管理台登录密码,留空回退 WEBUI_PASSWORD 环境变量或默认 admin;修改保存后立即生效,需用新账号重新登录 ",-1))]),_:1})]),_:1})]),_:1}),s("div",Ke,[a(f,{type:"primary",loading:A.value,disabled:A.value,onClick:b},{default:n(()=>[i($(A.value?"保存中...":"保存设置"),1)]),_:1},8,["loading","disabled"])])])),[[O,v.value]])]),_:1}),a(S,{label:"代理开关",name:"harness",lazy:""},{default:n(()=>[a(fe)]),_:1}),a(S,{label:"服务与更新",name:"service",lazy:""},{default:n(()=>[a(Se)]),_:1})]),_:1},8,["modelValue"])}}});export{Re as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as J,p as O,ab as Q,e as S,g as a,w as l,r as i,a as r,o as s,f as k,F as x,h as o,c as p,i as K,t as I,Z as W,l as z,A,aa as v,ac as X,m as Y,_ as h}from"./index-sPrBBnf5.js";const ee={class:"setup-wrap"},ae={class:"wizard-actions"},le={class:"key-row"},te={class:"wizard-actions"},se={class:"wizard-actions"},oe=J({__name:"SetupView",setup(ne){const P=Y(),m=i(0),n=i(""),d=i("generate"),b=i(""),M=i(""),_=i(""),c=i(""),U=i(""),B=i(!1),E=i(!1),w=i(!1);O(async()=>{const u=await Q();w.value=u.masterKeySet,w.value&&(d.value="skip")});const T=z(()=>n.value==="local"||w.value),R=z(()=>w.value?"(保留现有主密钥)":"(/v1 保持免鉴权)"),D=z(()=>d.value==="skip"?T.value:d.value==="custom"?M.value.trim().length>=8:b.value!=="");async function H(){try{const u=await A.post("/api/setup/master-key",{mode:"generate"});b.value=u.masterKey}catch(u){v.error(u.message||"生成失败")}}async function Z(){try{await navigator.clipboard.writeText(b.value),v.success("已复制到剪贴板")}catch{v.error("复制失败,请手动选择复制")}}async function j(){if(d.value==="skip"){m.value=2;return}B.value=!0;try{d.value==="custom"&&await A.post("/api/setup/master-key",{mode:"custom",masterKey:M.value.trim()}),w.value=!0,m.value=2}catch(u){v.error(u.message||"保存失败")}finally{B.value=!1}}async function $(){const u=_.value!==""||c.value!=="";if(u||n.value==="server"){if(!_.value.trim()){v.warning("请输入用户名");return}if(c.value.length<6){v.warning("密码至少 6 位");return}if(c.value!==U.value){v.warning("两次输入的密码不一致");return}}E.value=!0;try{await A.post("/api/setup/finish",{deployment:n.value,...u?{username:_.value.trim(),password:c.value}:{}}),X(n.value),v.success("初始化完成,请使用新账号登录"),await P.push("/login")}catch(e){v.error(e.message||"保存失败")}finally{E.value=!1}}return(u,e)=>{const L=r("el-step"),q=r("el-steps"),g=r("el-radio"),F=r("el-radio-group"),f=r("el-form-item"),N=r("el-form"),V=r("el-alert"),y=r("el-button"),C=r("el-input"),G=r("el-card");return s(),S("div",ee,[a(G,{shadow:"hover",class:"setup-card"},{default:l(()=>[e[20]||(e[20]=k("h2",{class:"setup-title"},"ELLM 初始化向导",-1)),a(q,{active:m.value,"align-center":"","finish-status":"success",class:"mb-24"},{default:l(()=>[a(L,{title:"部署环境"}),a(L,{title:"主密钥"}),a(L,{title:"登录账号"})]),_:1},8,["active"]),m.value===0?(s(),S(x,{key:0},[a(N,{"label-position":"top"},{default:l(()=>[a(f,{label:"这台机器上的 ELLM 会被哪些设备访问?"},{default:l(()=>[a(F,{modelValue:n.value,"onUpdate:modelValue":e[0]||(e[0]=t=>n.value=t)},{default:l(()=>[a(g,{value:"local"},{default:l(()=>[...e[9]||(e[9]=[o("仅本机使用(本地个人部署)",-1)])]),_:1}),a(g,{value:"server"},{default:l(()=>[...e[10]||(e[10]=[o("局域网 / 公网可访问(服务器部署)",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1})]),_:1}),n.value==="server"?(s(),p(V,{key:0,type:"warning","show-icon":"",closable:!1,title:"服务器部署存在安全风险",description:"服务将暴露给网络中的其他设备:不设置主密钥,任何人都能消耗你的模型额度;不修改默认账号密码(admin/admin),陌生人可直接登录管理台查看供应商密钥与调用日志。后两步将强制要求设置。",class:"mb-16"})):n.value==="local"?(s(),p(V,{key:1,type:"info","show-icon":"",closable:!1,title:"本地模式",description:"仅本机可访问时,主密钥与登录账号均可在后续步骤跳过(保持 /v1 免鉴权与默认账号 admin/admin)。若日后开放给其他设备,请再回来补全。",class:"mb-16"})):K("",!0),k("div",ae,[e[12]||(e[12]=k("span",null,null,-1)),a(y,{type:"primary",disabled:!n.value,onClick:e[1]||(e[1]=t=>m.value=1)},{default:l(()=>[...e[11]||(e[11]=[o("下一步",-1)])]),_:1},8,["disabled"])])],64)):m.value===1?(s(),S(x,{key:1},[w.value?(s(),p(V,{key:0,type:"success","show-icon":"",closable:!1,title:"已检测到主密钥",description:"当前部署已设置主密钥(可在「设置」页查看修改),本步可直接跳过;如需更换可在下方重新生成或输入。",class:"mb-16"})):K("",!0),a(N,{"label-position":"top"},{default:l(()=>[a(f,{label:"主密钥(调用 /v1/* 接口的 Bearer Token)"},{default:l(()=>[a(F,{modelValue:d.value,"onUpdate:modelValue":e[2]||(e[2]=t=>d.value=t)},{default:l(()=>[a(g,{value:"generate"},{default:l(()=>[...e[13]||(e[13]=[o("随机生成(推荐)",-1)])]),_:1}),a(g,{value:"custom"},{default:l(()=>[...e[14]||(e[14]=[o("自定义输入",-1)])]),_:1}),T.value?(s(),p(g,{key:0,value:"skip"},{default:l(()=>[o("跳过"+I(R.value),1)]),_:1})):K("",!0)]),_:1},8,["modelValue"])]),_:1}),d.value==="generate"?(s(),S(x,{key:0},[b.value?(s(),p(f,{key:1,label:"请复制保存你的主密钥(设置页也可随时查看):"},{default:l(()=>[k("div",le,[a(C,{"model-value":b.value,readonly:""},{append:l(()=>[a(y,{onClick:Z},{default:l(()=>[...e[16]||(e[16]=[o("复制",-1)])]),_:1})]),_:1},8,["model-value"])])]),_:1})):(s(),p(f,{key:0},{default:l(()=>[a(y,{type:"primary",plain:"",onClick:H},{default:l(()=>[...e[15]||(e[15]=[o("生成随机密钥",-1)])]),_:1})]),_:1}))],64)):K("",!0),d.value==="custom"?(s(),p(f,{key:1,label:"自定义主密钥(至少 8 位)"},{default:l(()=>[a(C,{modelValue:M.value,"onUpdate:modelValue":e[3]||(e[3]=t=>M.value=t),placeholder:"例如 my-secret-key-2026","show-password":"",clearable:""},null,8,["modelValue"])]),_:1})):K("",!0)]),_:1}),k("div",te,[a(y,{onClick:e[4]||(e[4]=t=>m.value=0)},{default:l(()=>[...e[17]||(e[17]=[o("上一步",-1)])]),_:1}),a(y,{type:"primary",loading:B.value,disabled:!D.value,onClick:j},{default:l(()=>[...e[18]||(e[18]=[o(" 下一步 ",-1)])]),_:1},8,["loading","disabled"])])],64)):(s(),S(x,{key:2},[n.value==="server"?(s(),p(V,{key:0,type:"warning","show-icon":"",closable:!1,title:"必须设置登录账号密码",description:"服务器部署的管理台会暴露在局域网/公网中,默认账号 admin/admin 等于向所有人开放管理权限,请务必设置强密码。",class:"mb-16"})):(s(),p(V,{key:1,type:"info","show-icon":"",closable:!1,title:"可选步骤",description:"本地模式可跳过,保持默认账号 admin/admin。建议设置专属账号密码,避免日后开放访问时遗忘修改。",class:"mb-16"})),a(N,{"label-position":"top",onSubmit:W($,["prevent"])},{default:l(()=>[a(f,{label:"用户名"},{default:l(()=>[a(C,{modelValue:_.value,"onUpdate:modelValue":e[5]||(e[5]=t=>_.value=t),placeholder:"请输入用户名",clearable:""},null,8,["modelValue"])]),_:1}),a(f,{label:"密码(至少 6 位)"},{default:l(()=>[a(C,{modelValue:c.value,"onUpdate:modelValue":e[6]||(e[6]=t=>c.value=t),type:"password","show-password":"",placeholder:"请输入密码"},null,8,["modelValue"])]),_:1}),a(f,{label:"确认密码"},{default:l(()=>[a(C,{modelValue:U.value,"onUpdate:modelValue":e[7]||(e[7]=t=>U.value=t),type:"password","show-password":"",placeholder:"请再次输入密码"},null,8,["modelValue"])]),_:1})]),_:1}),k("div",se,[a(y,{onClick:e[8]||(e[8]=t=>m.value=1)},{default:l(()=>[...e[19]||(e[19]=[o("上一步",-1)])]),_:1}),a(y,{type:"primary",loading:E.value,onClick:$},{default:l(()=>[o(I(n.value==="server"||_.value||c.value?"完成":"跳过并完成"),1)]),_:1},8,["loading"])])],64))]),_:1})])}}}),ie=h(oe,[["__scopeId","data-v-89c00f65"]]);export{ie as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.setup-wrap[data-v-89c00f65]{min-height:100vh;display:flex;align-items:center;justify-content:center;background:#f5f7fa}.setup-card[data-v-89c00f65]{width:560px;max-width:92vw}.setup-title[data-v-89c00f65]{text-align:center;margin-bottom:24px;color:#3b82f6}.key-row[data-v-89c00f65]{width:100%}.wizard-actions[data-v-89c00f65]{display:flex;justify-content:space-between;margin-top:8px}.mb-24[data-v-89c00f65]{margin-bottom:24px}.mb-16[data-v-89c00f65]{margin-bottom:16px}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a9 as w}from"./index-sPrBBnf5.js";var T;const h=((T=window.__ELLM_CONFIG__)==null?void 0:T.API_BASE)??"",f=3e4;let u=null;function E(){return u===null&&(u=localStorage.getItem("webui_token")),u}function S(){u=null,localStorage.removeItem("webui_token"),w.push("/login")}async function e(t,c,n,p=f){const i={"Content-Type":"application/json"},a=E();a&&(i.Authorization=`Bearer ${a}`);const s=new AbortController,o=setTimeout(()=>s.abort(),p);try{const r=await fetch(`${h}${c}`,{method:t,headers:i,body:n!==void 0?JSON.stringify(n):void 0,signal:s.signal});if(r.status===401)throw S(),new Error("未登录");const d=await r.text();let l;try{l=d?JSON.parse(d):{}}catch{throw new Error(`响应格式错误 (HTTP ${r.status})`)}if(!r.ok||l.ok===!1)throw new Error(l.error||`HTTP ${r.status}`);return l}catch(r){throw r.name==="AbortError"?new Error("请求超时"):r instanceof TypeError?new Error("服务不可达(重启窗口期或未启动,请稍后重试)"):r}finally{clearTimeout(o)}}const P={status:()=>e("GET","/status"),processes:()=>e("GET","/processes").then(t=>t.processes),start:()=>e("POST","/start"),stop:()=>e("POST","/stop"),restart:()=>e("POST","/restart"),autostart:()=>e("GET","/autostart"),setAutostart:t=>e("POST","/autostart",{enabled:t}),update:()=>e("GET","/update"),setUpdateEnabled:t=>e("POST","/update/enabled",{enabled:t}),checkUpdate:()=>e("POST","/update/check"),applyUpdate:()=>e("POST","/update/apply"),harness:()=>e("GET","/harness"),setClaudeCode:t=>e("POST","/harness/claude-code",{enabled:t}),setCodex:t=>e("POST","/harness/codex",{enabled:t}),setDsh:t=>e("POST","/harness/dsh",{enabled:t})};async function A(t){const{targetVersion:c,sinceAppliedAt:n,timeoutMs:p=600*1e3,pollMs:i=3e3}=t,a=Date.now()+p;for(;;){if(Date.now()>=a)return null;await new Promise(r=>setTimeout(r,i));let s=null;try{s=await P.update()}catch{continue}const o=s.lastApply;if(o&&o.toVersion===c&&(o.appliedAt??0)>(n??0))return o;if(Date.now()>=a)return null}}export{P as m,A as w};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{aa as o}from"./index-sPrBBnf5.js";function u(){function s(t,a="success"){o({message:t,type:a,duration:3e3})}return{toast:s}}export{u};
|