ellm-proxy 0.0.3 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +60 -60
  2. package/bin/commands/autostart.js +37 -213
  3. package/bin/commands/config.js +8 -8
  4. package/bin/commands/pm2.js +28 -28
  5. package/bin/commands/service.js +107 -107
  6. package/bin/ellm-proxy.js +61 -61
  7. package/dist/server.js +33 -2
  8. package/dist/web-ui/assets/DashboardView-BHG4xIdN.css +1 -0
  9. package/dist/web-ui/assets/{DashboardView-B71Dy3px.js → DashboardView-C3Hw-HpG.js} +1 -1
  10. package/dist/web-ui/assets/LayoutView-BUA8-IzK.css +1 -0
  11. package/dist/web-ui/assets/LayoutView-C9phf7in.js +1 -0
  12. package/dist/web-ui/assets/LoginView-DrhfslIY.css +1 -0
  13. package/dist/web-ui/assets/LoginView-es6pSPgt.js +1 -0
  14. package/dist/web-ui/assets/{LogsView-B2ctrpv4.js → LogsView-BlXITEJH.js} +1 -1
  15. package/dist/web-ui/assets/ProcessView-Bgf7-MLQ.js +1 -0
  16. package/dist/web-ui/assets/ProcessView-DK0TvDpR.css +1 -0
  17. package/dist/web-ui/assets/{ProvidersView-mb0KU16n.js → ProvidersView-f8aZkMdw.js} +1 -1
  18. package/dist/web-ui/assets/SettingsView-BkwyoOUb.css +1 -0
  19. package/dist/web-ui/assets/SettingsView-GvAA2vLp.js +1 -0
  20. package/dist/web-ui/assets/config-Cs3_z63C.js +1 -0
  21. package/dist/web-ui/assets/{index-dPeMJjrz.js → index-GohB4JDa.js} +2 -2
  22. package/dist/web-ui/assets/{providers-Dmz2p4Wd.js → providers-Djtdy2tv.js} +1 -1
  23. package/dist/web-ui/assets/{useApi-C1Ngt_o5.js → useApi-D_iJ2Mca.js} +1 -1
  24. package/dist/web-ui/assets/useManagerApi-C1_COxKg.js +1 -0
  25. package/dist/web-ui/assets/{useToast-B3quFZyv.js → useToast-C0UMTbkx.js} +1 -1
  26. package/dist/web-ui/config.js +10 -10
  27. package/dist/web-ui/index.html +13 -13
  28. package/docs/DESIGN.md +99 -99
  29. package/manager/api/harness/backup.js +20 -20
  30. package/manager/api/harness/claude-code.js +54 -54
  31. package/manager/api/harness/codex.js +123 -123
  32. package/manager/api/harness/dsh.js +175 -175
  33. package/manager/api/harness/index.js +118 -118
  34. package/manager/api/service-settings.js +98 -0
  35. package/manager/api/service.js +3 -13
  36. package/manager/index.js +182 -170
  37. package/package.json +4 -1
  38. package/utils/autostart.js +219 -0
  39. package/utils/interpreter.js +14 -14
  40. package/utils/paths.js +45 -44
  41. package/utils/pm2.js +130 -130
  42. package/utils/service.js +205 -189
  43. package/utils/updater.js +210 -0
  44. package/dist/web-ui/assets/DashboardView-qrQrkgLA.css +0 -1
  45. package/dist/web-ui/assets/LayoutView-C03GVB3X.js +0 -1
  46. package/dist/web-ui/assets/LoginView-Dg0mB6ok.css +0 -1
  47. package/dist/web-ui/assets/LoginView-XTF5dHvd.js +0 -1
  48. package/dist/web-ui/assets/ProcessView-DmbVABQV.js +0 -1
  49. package/dist/web-ui/assets/ProcessView-DzMqBJmd.css +0 -1
  50. package/dist/web-ui/assets/SettingsView-BDkymsrS.css +0 -1
  51. package/dist/web-ui/assets/SettingsView-BgLQVC2H.js +0 -1
  52. package/dist/web-ui/assets/config-BRaXq0LL.js +0 -1
  53. package/dist/web-ui/assets/useManagerApi-BDWJz5o6.js +0 -1
@@ -1 +0,0 @@
1
- import{d as b,c as h,a as o,w as l,r as a,o as _,b as k,W as x,a0 as f,j as C,k as L,e as B,l as i,g as N,_ as I}from"./index-dPeMJjrz.js";import{A as K}from"./useApi-C1Ngt_o5.js";const A={class:"login-wrap"},E=b({__name:"LoginView",setup(M){const v=N(),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 K.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 c=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 _(),h("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(c,{modelValue:n.value,"onUpdate:modelValue":e[0]||(e[0]=p=>n.value=p),placeholder:"请输入用户名",onKeyup:f(r,["enter"])},null,8,["modelValue"])]),_:1}),o(m,{label:"密码"},{default:l(()=>[o(c,{modelValue:s.value,"onUpdate:modelValue":e[1]||(e[1]=p=>s.value=p),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})])}}}),j=I(E,[["__scopeId","data-v-d5b6a90c"]]);export{j as default};
@@ -1 +0,0 @@
1
- import{d as K,i as q,a6 as H,n as J,c as D,a as n,w as l,l as i,r as c,m as Q,o as w,b as d,X as W,e as o,t as r,j as Y,a1 as Z,_ as ee}from"./index-dPeMJjrz.js";import{m as I}from"./useManagerApi-BDWJz5o6.js";import{u as te}from"./useToast-B3quFZyv.js";const ae={class:"flex-col-gap"},le={class:"flex-between"},ne={class:"proc-name"},se={class:"proc-sub"},oe=["onClick"],re={key:1,class:"muted-sm"},ie={class:"desc-full"},de=K({__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()),P={daemon:"pm2 守护进程",manager:"管理服务",main:"代理服务"},C=e=>P[e.role]??e.role,k={daemon:"pm2 守护进程:托管代理服务与管理服务的后台守护,进程崩溃时自动拉起;自身不监听端口、不处理业务请求,不允许远程操控(停止它将失去自动恢复能力)。",manager:"管理服务(ellm-proxy-manager):独立常驻的轻量服务,提供进程启停接口与代理开关接口;代理服务停止时保持存活,保证还能在本页远程拉起代理服务。",main:"代理服务(ellm-proxy):网关本体,转发 /v1/* 模型请求,同时承载管理台页面与 API;本页的重启操作即作用于该进程,重启期间网关与管理台 API 短暂不可用(代理服务停止时重启等效于启动)。"},A=e=>k[e.role],_=i(!1),T=i(""),M=i(""),E=e=>{T.value=`${C(e)}(${$(e)})`,M.value=k[e.role],_.value=!0},$=e=>e.role==="daemon"?"pm2":e.role==="manager"?"ellm-proxy-manager":"ellm-proxy",O=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(!O(e)||!e.startedAt)return"-";let t=Math.max(0,Math.floor((h.value-e.startedAt)/1e3));const m=Math.floor(t/86400);t-=m*86400;const u=Math.floor(t/3600);t-=u*3600;const s=Math.floor(t/60);return t-=s*60,[m?`${m}天`:"",u?`${u}时`:"",s?`${s}分`:"",`${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":"-",f=async e=>{e||(y.value.length?g.value=!0:v.value=!0);try{y.value=await I.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||f(!0)};q(()=>{f(!1),B=window.setInterval(j,5e3)}),H(()=>window.clearInterval(B));const z=async()=>{try{await Z.confirm("确定重启 ellm-proxy 代理服务?重启期间网关与管理台 API 将短暂不可用(代理服务已停止时重启等效于启动)。","重启确认",{confirmButtonText:"重启",cancelButtonText:"取消",type:"warning"})}catch{return}p.value="restart";try{await I.restart(),b("重启完成","success")}catch(e){b(e.message||"操作失败","error")}finally{p.value=null,await f(!0)}};return(e,t)=>{const m=c("el-tag"),u=c("el-button"),s=c("el-table-column"),F=c("el-empty"),L=c("el-table"),U=c("el-card"),X=c("el-dialog"),G=Q("loading");return J((w(),D("div",ae,[n(U,{shadow:"never"},{header:l(()=>[d("div",le,[t[6]||(t[6]=d("span",{class:"fw-600"},"进程管理",-1)),d("div",null,[n(m,{type:"info",class:"mr-8"},{default:l(()=>[...t[4]||(t[4]=[o("刷新间隔 5s",-1)])]),_:1}),n(u,{loading:g.value,onClick:t[0]||(t[0]=a=>f(!1))},{default:l(()=>[...t[5]||(t[5]=[o("刷新",-1)])]),_:1},8,["loading"])])])]),default:l(()=>[n(L,{data:y.value,border:"",stripe:"",class:"full-width"},{empty:l(()=>[n(F,{description:x.value||"暂无进程数据"},{default:l(()=>[n(u,{type:"primary",onClick:t[1]||(t[1]=a=>f(!1))},{default:l(()=>[...t[8]||(t[8]=[o("重试",-1)])]),_:1})]),_:1},8,["description"])]),default:l(()=>[n(s,{label:"进程","min-width":"180"},{default:l(({row:a})=>[d("div",ne,[d("span",{class:W(["status-dot",N(a)])},null,2),o(" "+r(C(a))+" ",1),d("span",se,r($(a)),1)])]),_:1}),n(s,{label:"运行状态",width:"100",align:"center"},{default:l(({row:a})=>[n(m,{type:V(a).type,"disable-transitions":""},{default:l(()=>[o(r(V(a).text),1)]),_:2},1032,["type"])]),_:1}),n(s,{label:"PID",width:"90",align:"center"},{default:l(({row:a})=>[o(r(a.pid??"-"),1)]),_:1}),n(s,{label:"端口",width:"90",align:"center"},{default:l(({row:a})=>[o(r(a.port??"-"),1)]),_:1}),n(s,{label:"运行时长",width:"130",align:"center"},{default:l(({row:a})=>[o(r(R(a)),1)]),_:1}),n(s,{label:"地址","min-width":"170"},{default:l(({row:a})=>[o(r(a.address??"-"),1)]),_:1}),n(s,{label:"内存",width:"90",align:"center"},{default:l(({row:a})=>[o(r(S(a.memory)),1)]),_:1}),n(s,{label:"描述","min-width":"220"},{default:l(({row:a})=>[d("span",{class:"desc-cell",title:"点击查看详情",onClick:ce=>E(a)},r(A(a)),9,oe)]),_:1}),n(s,{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"}," 进程控制由管理服务(manager)执行,仅代理服务(ellm-proxy)可重启(已停止时重启等效于启动): 重启期间网关与管理台 API 短暂不可用。 pm2 守护进程与管理服务不允许远程操控(停止它们将失去远程恢复能力)。 ",-1))]),_:1}),n(X,{modelValue:_.value,"onUpdate:modelValue":t[3]||(t[3]=a=>_.value=a),title:T.value,width:"520px"},{footer:l(()=>[n(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(M.value),1)]),_:1},8,["modelValue","title"])])),[[G,v.value]])}}}),_e=ee(de,[["__scopeId","data-v-1404bf7a"]]);export{_e as default};
@@ -1 +0,0 @@
1
- @charset "UTF-8";.proc-name[data-v-1404bf7a]{display:flex;align-items:center;gap:8px;font-weight:600}.proc-name .proc-sub[data-v-1404bf7a]{font-weight:400;color:var(--el-text-color-secondary);font-size:12px}.status-dot[data-v-1404bf7a]{display:inline-block;width:8px;height:8px;border-radius:50%}.status-dot.running[data-v-1404bf7a]{background:var(--el-color-success);box-shadow:0 0 4px var(--el-color-success)}.status-dot.stopped[data-v-1404bf7a]{background:var(--el-color-info)}.status-dot.abnormal[data-v-1404bf7a]{background:var(--el-color-danger)}.desc-cell[data-v-1404bf7a]{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.desc-full[data-v-1404bf7a]{margin:0;line-height:1.8}.mr-8[data-v-1404bf7a]{margin-right:8px}
@@ -1 +0,0 @@
1
- .switch-row[data-v-e8a0f661]{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:4px 0}.switch-title[data-v-e8a0f661]{display:flex;align-items:center;gap:8px;font-weight:600;margin-bottom:4px}.switch-desc[data-v-e8a0f661]{color:var(--el-text-color-secondary);font-size:13px;line-height:1.6}.switch-desc code[data-v-e8a0f661]{background:var(--el-fill-color-light);padding:1px 6px;border-radius:4px;font-size:12px}.switch-divider[data-v-e8a0f661]{margin:16px 0}
@@ -1 +0,0 @@
1
- import{u as D}from"./useToast-B3quFZyv.js";import{A as q}from"./useApi-C1Ngt_o5.js";import{u as G}from"./config-BRaXq0LL.js";import{d as N,i as H,n as R,o as x,j as T,w as o,c as U,b as t,t as A,k as B,e as n,a as s,F as L,l as d,a1 as J,r as i,m as M,_ as Q}from"./index-dPeMJjrz.js";import{m as P}from"./useManagerApi-BDWJz5o6.js";const Z={class:"flex-between"},ee={class:"muted-sm mb-8"},le={class:"switch-row"},se={class:"switch-info"},te={class:"switch-title"},oe={class:"switch-row"},ae={class:"switch-info"},ne={class:"switch-title"},ie={class:"switch-row"},ue={class:"switch-info"},de={class:"switch-title"},re=N({__name:"HarnessSwitches",setup(O){const{toast:h}=D(),v=d(!0),k=d(!1),u=d(null),g=d(null),c=d(null),p=d(null),y=d("success"),w=async m=>{m||(u.value?k.value=!0:v.value=!0);try{u.value=await P.harness(),g.value=null}catch(l){m||(g.value=l.message)}finally{m||(v.value=!1,k.value=!1)}};H(()=>w(!1));const E={claudeCode:{label:"Claude Code",file:"~/.claude/settings.json",toggle:P.setClaudeCode},codex:{label:"Codex CLI",file:"~/.codex/config.toml",toggle:P.setCodex},dsh:{label:"DeepSeek Harness",file:"~/.dsh/settings.yaml",toggle:P.setDsh}},b=async(m,l)=>{if(!u.value||c.value)return;const _=E[m],a=l?`确定开启 ${_.label} 代理?将改写 ${_.file}(开启前自动备份,关闭时从备份还原)。`:`确定关闭 ${_.label} 代理?将从备份还原 ${_.file}(无备份时移除代理相关配置)。`;try{await J.confirm(a,l?"开启确认":"关闭确认",{confirmButtonText:l?"开启":"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}c.value=m;try{const e=await _.toggle(l);p.value=e.note??null,y.value=l?"success":"info",h(l?"已开启代理":"已关闭代理","success")}catch(e){h(e.message||"操作失败","error")}finally{c.value=null,await w(!0)}};return(m,l)=>{const _=i("el-button"),a=i("el-alert"),e=i("el-tag"),V=i("el-switch"),S=i("el-divider"),C=i("el-empty"),$=i("el-card"),I=M("loading");return R((x(),T($,{shadow:"never"},{header:o(()=>[t("div",Z,[l[7]||(l[7]=t("span",{class:"fw-600"},"代理开关",-1)),s(_,{loading:k.value,onClick:l[0]||(l[0]=f=>w(!1))},{default:o(()=>[...l[6]||(l[6]=[n("刷新",-1)])]),_:1},8,["loading"])])]),default:o(()=>[u.value?(x(),U(L,{key:0},[t("div",ee," 将本机 CLI 工具的流量一键指向 ellm-proxy 网关("+A(u.value.gatewayBase)+")。 开启前自动备份原配置为 *.ellm-bak(保留最早的原始备份),关闭时从备份还原。 ",1),p.value?(x(),T(a,{key:0,title:p.value,type:y.value,"show-icon":"",class:"mb-8",onClose:l[1]||(l[1]=f=>p.value=null)},null,8,["title","type"])):B("",!0),t("div",le,[t("div",se,[t("div",te,[l[8]||(l[8]=n(" Claude Code ",-1)),s(e,{type:u.value.claudeCode?"success":"info",size:"small","disable-transitions":""},{default:o(()=>[n(A(u.value.claudeCode?"已开启":"未开启"),1)]),_:1},8,["type"])]),l[9]||(l[9]=t("div",{class:"switch-desc"},[n(" 写入 "),t("code",null,"~/.claude/settings.json"),n(" 的 env.ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN, 鉴权取网关配置的 masterKey(未设置将开启失败);其余字段原样保留,重启 Claude Code 会话后生效。 ")],-1))]),s(V,{"model-value":u.value.claudeCode,loading:c.value==="claudeCode",onChange:l[2]||(l[2]=f=>b("claudeCode",!!f))},null,8,["model-value","loading"])]),s(S,{class:"switch-divider"}),t("div",oe,[t("div",ae,[t("div",ne,[l[10]||(l[10]=n(" Codex CLI ",-1)),s(e,{type:u.value.codex?"success":"info",size:"small","disable-transitions":""},{default:o(()=>[n(A(u.value.codex?"已开启":"未开启"),1)]),_:1},8,["type"])]),l[11]||(l[11]=t("div",{class:"switch-desc"},[n(" 写入 "),t("code",null,"~/.codex/config.toml"),n(" 的 model_provider 与 [model_providers.ellm-proxy] 段 (保留用户注释与排版);开启后需按提示设置环境变量 ELLM_PROXY_API_KEY 供 Codex 鉴权。 ")],-1))]),s(V,{"model-value":u.value.codex,loading:c.value==="codex",onChange:l[3]||(l[3]=f=>b("codex",!!f))},null,8,["model-value","loading"])]),s(S,{class:"switch-divider"}),t("div",ie,[t("div",ue,[t("div",de,[l[12]||(l[12]=n(" DeepSeek Harness ",-1)),s(e,{type:u.value.dsh?"success":"info",size:"small","disable-transitions":""},{default:o(()=>[n(A(u.value.dsh?"已开启":"未开启"),1)]),_:1},8,["type"])]),l[13]||(l[13]=t("div",{class:"switch-desc"},[n(" 写入 "),t("code",null,"~/.dsh/settings.yaml"),n(" 的 llm-deepseek.baseURL 与 "),t("code",null,"~/.dsh/.credentials.yaml"),n(" 的 DEEPSEEK_API_KEY(取网关 masterKey,未设置则写占位值);模型请在 dsh 内选择与网关部署名一致的模型。 dsh 为 developer preview,配置格式可能随版本变化。 ")],-1))]),s(V,{"model-value":u.value.dsh,loading:c.value==="dsh",onChange:l[4]||(l[4]=f=>b("dsh",!!f))},null,8,["model-value","loading"])])],64)):(x(),T(C,{key:1,description:g.value||"暂无开关状态"},{default:o(()=>[s(_,{type:"primary",onClick:l[5]||(l[5]=f=>w(!1))},{default:o(()=>[...l[14]||(l[14]=[n("重试",-1)])]),_:1})]),_:1},8,["description"]))]),_:1})),[[I,v.value]])}}}),me=Q(re,[["__scopeId","data-v-e8a0f661"]]),ve={class:"flex-col-gap"},ce={class:"flex-between"},pe={class:"muted-sm"},_e={key:0},fe={class:"flex-end"},xe=N({__name:"SettingsView",setup(O){const{toast:h}=D(),v=G(),k=d("basic"),u=d(!0),g=d(!1),c=d(""),p=d(29770),y=d(null),w=d("passthrough"),E=d(!1),b=d(""),m=d(""),l=async()=>{var a;try{await v.load(),c.value=v.config.masterKey||"",p.value=v.config.port??29770,w.value=v.config.compression==="plain"?"plain":"passthrough",E.value=v.config.logContent===!0;const e=(a=v.config["web-ui"])==null?void 0:a["login-account"];b.value=(e==null?void 0:e.username)||"",m.value=(e==null?void 0:e.password)||"";try{y.value=await q.get("/api/status").then(V=>V.listenPort)}catch{y.value=null}}catch(e){h(e.message,"error")}finally{u.value=!1}},_=async()=>{g.value=!0;try{await v.update(a=>{var e;a.masterKey=c.value.trim(),a.port=p.value,a.compression=w.value,a.logContent=E.value,a["web-ui"]??(a["web-ui"]={}),(e=a["web-ui"])["login-account"]??(e["login-account"]={}),a["web-ui"]["login-account"].username=b.value.trim(),a["web-ui"]["login-account"].password=m.value}),h("设置已保存并生效","success")}catch(a){h(a.message,"error")}finally{g.value=!1}};return H(l),(a,e)=>{const V=i("el-tag"),S=i("el-input"),C=i("el-form-item"),$=i("el-input-number"),I=i("el-option"),f=i("el-select"),z=i("el-switch"),W=i("el-form"),j=i("el-card"),Y=i("el-button"),K=i("el-tab-pane"),F=i("el-tabs"),X=M("loading");return x(),T(F,{modelValue:k.value,"onUpdate:modelValue":e[6]||(e[6]=r=>k.value=r),class:"settings-tabs"},{default:o(()=>[s(K,{label:"基本设置",name:"basic"},{default:o(()=>[R((x(),U("div",ve,[s(j,{shadow:"never"},{header:o(()=>[t("div",ce,[e[8]||(e[8]=t("span",{class:"fw-600"},"基本设置",-1)),s(V,{type:"success"},{default:o(()=>[...e[7]||(e[7]=[n("运行中",-1)])]),_:1})])]),default:o(()=>[s(W,{"label-position":"top"},{default:o(()=>[s(C,{label:"Master Key"},{default:o(()=>[s(S,{modelValue:c.value,"onUpdate:modelValue":e[0]||(e[0]=r=>c.value=r),type:"password",placeholder:"sk-1234","show-password":""},null,8,["modelValue"]),e[9]||(e[9]=t("div",{class:"muted-sm"}," 网关 API 认证密钥(/v1/* 请求需携带 Bearer <masterKey>;留空 = 本地放行) ",-1))]),_:1}),s(C,{label:"监听端口 (port)"},{default:o(()=>[s($,{modelValue:p.value,"onUpdate:modelValue":e[1]||(e[1]=r=>p.value=r),min:1,max:65535,step:1,class:"full-width"},null,8,["modelValue"]),t("div",pe,[e[11]||(e[11]=n(" 网关与管理台 API 的监听端口,保存后下次重启生效 ",-1)),y.value!==null?(x(),U(L,{key:0},[e[10]||(e[10]=n(" ;当前实际监听 ",-1)),t("b",null,A(y.value),1),y.value!==p.value?(x(),U("span",_e,"(由 PORT 环境变量覆盖,此配置值不生效)")):B("",!0)],64)):B("",!0)])]),_:1}),s(C,{label:"压缩语义 (compression)"},{default:o(()=>[s(f,{modelValue:w.value,"onUpdate:modelValue":e[2]||(e[2]=r=>w.value=r),class:"full-width"},{default:o(()=>[s(I,{label:"透传 (passthrough)",value:"passthrough"}),s(I,{label:"明文 (plain)",value:"plain"})]),_:1},8,["modelValue"]),e[12]||(e[12]=t("div",{class:"muted-sm"},"上游响应压缩处理方式,保存后热更新生效",-1))]),_:1}),s(C,{label:"日志内容捕获 (logContent)"},{default:o(()=>[s(z,{modelValue:E.value,"onUpdate:modelValue":e[3]||(e[3]=r=>E.value=r)},null,8,["modelValue"]),e[13]||(e[13]=t("div",{class:"muted-sm"}," 记录完整请求/响应(含上游原始与转换后内容)进日志详情,用于排查定位问题;开启后有少量内存与存储开销,保存后热更新生效 ",-1))]),_:1}),s(C,{label:"管理台用户名 (web-ui.login-account.username)"},{default:o(()=>[s(S,{modelValue:b.value,"onUpdate:modelValue":e[4]||(e[4]=r=>b.value=r),placeholder:"admin"},null,8,["modelValue"]),e[14]||(e[14]=t("div",{class:"muted-sm"}," 管理台登录用户名,留空回退 WEBUI_USERNAME 环境变量或默认 admin ",-1))]),_:1}),s(C,{label:"管理台密码 (web-ui.login-account.password)"},{default:o(()=>[s(S,{modelValue:m.value,"onUpdate:modelValue":e[5]||(e[5]=r=>m.value=r),type:"password",placeholder:"admin","show-password":""},null,8,["modelValue"]),e[15]||(e[15]=t("div",{class:"muted-sm"}," 管理台登录密码,留空回退 WEBUI_PASSWORD 环境变量或默认 admin;修改保存后立即生效,需用新账号重新登录 ",-1))]),_:1})]),_:1})]),_:1}),t("div",fe,[s(Y,{type:"primary",loading:g.value,disabled:g.value,onClick:_},{default:o(()=>[n(A(g.value?"保存中...":"保存设置"),1)]),_:1},8,["loading","disabled"])])])),[[X,u.value]])]),_:1}),s(K,{label:"代理开关",name:"harness",lazy:""},{default:o(()=>[s(me)]),_:1})]),_:1},8,["modelValue"])}}});export{xe as default};
@@ -1 +0,0 @@
1
- import{U as p,l as u,f as n}from"./index-dPeMJjrz.js";import{A as i}from"./useApi-C1Ngt_o5.js";const m=p("config",()=>{const e=u({}),o=u(!1),t=n(()=>Array.isArray(e.value.providers)?e.value.providers:[]),l=n(()=>t.value.length),c=n(()=>t.value.reduce((a,d)=>{var s;return a+(((s=d.models)==null?void 0:s.length)??0)},0));async function f(){o.value=!0;try{e.value=await i.get("/api/config")}finally{o.value=!1}}async function r(){return await i.put("/api/config",e.value)}async function v(a){return a(e.value),await r()}return{config:e,loading:o,providers:t,providerCount:l,totalModelCount:c,load:f,save:r,update:v}});export{m as u};
@@ -1 +0,0 @@
1
- var l;const c=((l=window.__ELLM_CONFIG__)==null?void 0:l.API_BASE)??"";function u(){try{const t=c?new URL(c):new URL(location.href);return t.port?`${t.protocol}//${t.hostname}:${Number(t.port)+1}`:null}catch{return null}}var h;const i=((h=window.__ELLM_CONFIG__)==null?void 0:h.MANAGER_BASE)??u(),T=3e4;async function r(t,E,s){if(i===null)throw new Error("无法确定管理服务地址:config.js 未配置 MANAGER_BASE,且页面经 80/443 端口访问(无法按端口+1 推导)");const n=new AbortController,w=setTimeout(()=>n.abort(),T);try{const e=await fetch(`${i}${E}`,{method:t,headers:{"Content-Type":"application/json"},body:s!==void 0?JSON.stringify(s):void 0,signal:n.signal}),a=await e.text();let o;try{o=a?JSON.parse(a):{}}catch{throw new Error(`管理服务响应格式错误 (HTTP ${e.status})`)}if(!e.ok||o.ok===!1)throw new Error(o.error||`HTTP ${e.status}`);return o}catch(e){throw e.name==="AbortError"?new Error("请求超时"):e instanceof TypeError?new Error("管理服务不可达(请确认 ellm-proxy 已启动,且本页面通过 localhost/127.0.0.1 访问)"):e}finally{clearTimeout(w)}}const p={status:()=>r("GET","/status"),processes:()=>r("GET","/processes").then(t=>t.processes),start:()=>r("POST","/start"),stop:()=>r("POST","/stop"),restart:()=>r("POST","/restart"),harness:()=>r("GET","/harness"),setClaudeCode:t=>r("POST","/harness/claude-code",{enabled:t}),setCodex:t=>r("POST","/harness/codex",{enabled:t}),setDsh:t=>r("POST","/harness/dsh",{enabled:t})};export{p as m};