diff --git a/crates/proxy/admin-ui/dist/index.html b/crates/proxy/admin-ui/dist/index.html index 3407285..e6dfc0e 100644 --- a/crates/proxy/admin-ui/dist/index.html +++ b/crates/proxy/admin-ui/dist/index.html @@ -4,23 +4,23 @@ Proxy Admin - +PROXY_API_KEYS=my-key`})]})]})]}),_&&(0,F.jsxs)(H,{className:`settings-restart-banner`,children:[(0,F.jsx)(`span`,{children:`Restart the proxy for imported env vars to take effect.`}),(0,F.jsx)(V,{size:`sm`,onClick:E,children:`Dismiss`})]}),(0,F.jsxs)(`div`,{style:{marginBottom:24},children:[(0,F.jsx)(`div`,{className:`section-label`,style:{marginBottom:8},children:`Env File`}),(0,F.jsxs)(`div`,{style:{display:`flex`,gap:8,alignItems:`center`},children:[(0,F.jsx)(`input`,{ref:c,type:`file`,accept:`.env,.anyllm.env,text/plain`,style:{display:`none`},onChange:w}),(0,F.jsx)(V,{size:`sm`,onClick:()=>c.current?.click(),disabled:s.isPending,loading:s.isPending,children:`Import .anyllm.env`}),(0,F.jsx)(V,{size:`sm`,onClick:T,children:`Export .anyllm.env`})]}),d&&(0,F.jsxs)(`div`,{style:{marginTop:10},children:[(0,F.jsxs)(`div`,{className:`dim`,style:{marginBottom:4},children:[d.applied,` variable`,d.applied===1?``:`s`,` imported.`,d.warnings.length===0&&` No issues.`]}),d.warnings.length>0&&(0,F.jsxs)(`div`,{style:{marginTop:8,padding:`8px 12px`,background:`var(--warn-dim)`,borderLeft:`3px solid var(--warn)`,borderRadius:`var(--r)`,fontSize:12},children:[(0,F.jsx)(`div`,{style:{fontWeight:600,marginBottom:4},children:`Warnings`}),d.warnings.map((e,t)=>(0,F.jsxs)(`div`,{className:`mono`,style:{fontSize:12},children:[e.line!=null&&(0,F.jsxs)(`span`,{className:`dim`,children:[`[line `,e.line,`] `]}),e.key&&(0,F.jsxs)(`span`,{children:[e.key,`: `]}),e.message]},t))]})]}),p&&(0,F.jsxs)(`div`,{style:{marginTop:10,padding:`8px 12px`,background:`var(--err-dim)`,borderLeft:`3px solid var(--err)`,borderRadius:`var(--r)`,fontSize:12},children:[(0,F.jsx)(`div`,{style:{fontWeight:600,marginBottom:4},children:`Import rejected`}),p.hard_errors.map((e,t)=>(0,F.jsx)(`div`,{className:`mono`,style:{fontSize:12},children:e},t)),p.warnings.length>0&&(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`div`,{style:{fontWeight:600,marginTop:8,marginBottom:4},children:`Warnings (from partial parse)`}),p.warnings.map((e,t)=>(0,F.jsxs)(`div`,{className:`mono`,style:{fontSize:12},children:[e.line!=null&&(0,F.jsxs)(`span`,{className:`dim`,children:[`[line `,e.line,`] `]}),e.message]},t))]})]}),h&&(0,F.jsxs)(`div`,{style:{marginTop:10,padding:`8px 12px`,background:`var(--err-dim)`,borderLeft:`3px solid var(--err)`,borderRadius:`var(--r)`,fontSize:12},children:[`Export failed: `,h]})]}),(0,F.jsx)(qs,{loading:n,error:r?.message}),t&&(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`div`,{className:`section-label`,style:{marginBottom:8},children:`Runtime`}),(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsxs)(`label`,{className:`form-label`,htmlFor:`cfg-redact-secrets`,style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,F.jsx)(`input`,{id:`cfg-redact-secrets`,type:`checkbox`,checked:t.redact_secrets,disabled:a.isPending,onChange:e=>C(`redact_secrets`,e.target.checked)}),`Redact secrets`]}),t.overridden_keys.includes(`redact_secrets`)&&(0,F.jsx)(`div`,{className:`form-row`,children:(0,F.jsx)(V,{size:`sm`,onClick:()=>b(`redact_secrets`),children:`Reset`})})]}),t.entries.filter(e=>e.key!==`redact_secrets`).map(e=>{let t=`cfg-${e.key}`;return(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsx)(`label`,{className:`form-label`,htmlFor:t,children:e.key}),(0,F.jsxs)(`div`,{className:`form-row`,children:[(0,F.jsx)(`input`,{id:t,name:e.key,value:l[e.key]??e.value,onChange:t=>u(n=>({...n,[e.key]:t.target.value}))}),(0,F.jsx)(V,{tone:`primary`,size:`sm`,onClick:()=>S(e.key,e.value),children:`Save`}),(0,F.jsx)(V,{size:`sm`,onClick:()=>b(e.key),children:`Reset`})]})]},e.key)})]}),i&&(0,F.jsxs)(`div`,{className:`readonly-section`,style:{marginTop:16},children:[(0,F.jsx)(`div`,{className:`section-label`,children:`Environment`}),(0,F.jsx)(`div`,{style:{display:`grid`,gridTemplateColumns:`220px 1fr`,gap:`4px 12px`,marginTop:8,fontSize:12},children:Object.entries(i).map(([e,t])=>(0,F.jsxs)(P.Fragment,{children:[(0,F.jsx)(`span`,{className:`dim`,children:e}),(0,F.jsx)(`span`,{className:`mono`,children:t})]},e))})]}),(0,F.jsx)(nc,{open:y!==null,onClose:()=>b(null),onConfirm:x,title:`Reset override?`,message:(0,F.jsxs)(F.Fragment,{children:[`Reset override for `,(0,F.jsx)(`span`,{className:`mono`,children:y}),`? The runtime value will revert to the env-file or default. Active connections are not affected.`]}),confirmLabel:`Reset`,variant:`primary`})]})}function oz({variant:e}){return(0,F.jsx)(`span`,{className:`badge badge-${e}`,children:e})}function sz({spent:e,limit:t}){if(!t)return(0,F.jsx)(`span`,{className:`dim`,children:`—`});let n=Math.min(e/t*100,100),r=n>=95?`danger`:n>=80?`warn`:``;return(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`div`,{className:`budget-bar`,children:(0,F.jsx)(`div`,{className:`budget-bar-fill${r?` ${r}`:``}`,style:{width:`${n}%`}})}),(0,F.jsxs)(`span`,{className:`dim`,style:{fontSize:10},children:[`$`,e.toFixed(4),` / $`,t.toFixed(2)]})]})}function cz({onCreated:e}){let t=Ra(),[n,r]=(0,P.useState)(``),[i,a]=(0,P.useState)(``),[o,s]=(0,P.useState)(``);function c(){t.mutate({description:n||null,spend_limit:i?Number(i):null,rpm_limit:o?Number(o):null},{onSuccess:t=>{r(``),a(``),s(``),e(t.key)}})}return(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsx)(`div`,{className:`form-label`,children:`Create Key`}),(0,F.jsx)(`form`,{onSubmit:e=>{e.preventDefault(),c()},children:(0,F.jsxs)(`div`,{className:`form-row`,style:{flexWrap:`wrap`},children:[(0,F.jsx)(`input`,{name:`description`,placeholder:`Description`,value:n,onChange:e=>r(e.target.value)}),(0,F.jsx)(`input`,{name:`spend_limit`,placeholder:`Spend limit USD`,type:`number`,value:i,onChange:e=>a(e.target.value),style:{width:120}}),(0,F.jsx)(`input`,{name:`rpm_limit`,placeholder:`RPM limit`,type:`number`,value:o,onChange:e=>s(e.target.value),style:{width:100}}),(0,F.jsx)(V,{type:`submit`,tone:`primary`,loading:t.isPending,children:`Create`})]})})]})}function lz({vk:e,onClose:t}){let n=za(),r=Ba(),[i,a]=(0,P.useState)(e.description??``),[o,s]=(0,P.useState)(e.spend_limit?.toString()??``),[c,l]=(0,P.useState)(e.rpm_limit?.toString()??``),[u,d]=(0,P.useState)(new Set(e.allowed_routes??[])),[f,p]=(0,P.useState)(!1),{data:m}=ao();function h(){n.mutate({id:e.id,body:{description:i||null,spend_limit:o?Number(o):null,rpm_limit:c?Number(c):null,allowed_routes:u.size>0?[...u]:null}},{onSuccess:t})}function g(){return r.mutateAsync(e.id).then(()=>{t()})}return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(tc,{open:!0,onClose:t,title:`Edit Key — ${e.key_prefix}…`,dismissable:!n.isPending,footer:(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(V,{tone:`danger`,onClick:()=>p(!0),disabled:n.isPending,style:{marginRight:`auto`},children:`Revoke`}),(0,F.jsx)(V,{onClick:t,disabled:n.isPending,children:`Cancel`}),(0,F.jsx)(V,{tone:`primary`,onClick:h,loading:n.isPending,children:`Save`})]}),children:[(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsx)(`label`,{className:`form-label`,htmlFor:`vk-desc`,children:`Description`}),(0,F.jsx)(`input`,{id:`vk-desc`,name:`description`,value:i,onChange:e=>a(e.target.value),style:{width:`100%`}})]}),(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsx)(`label`,{className:`form-label`,htmlFor:`vk-spend`,children:`Spend limit (USD)`}),(0,F.jsx)(`input`,{id:`vk-spend`,name:`spend_limit`,value:o,onChange:e=>s(e.target.value),type:`number`,min:`0`,step:`0.01`})]}),(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsx)(`label`,{className:`form-label`,htmlFor:`vk-rpm`,children:`RPM limit`}),(0,F.jsx)(`input`,{id:`vk-rpm`,name:`rpm_limit`,value:c,onChange:e=>l(e.target.value),type:`number`,min:`0`})]}),(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsxs)(`div`,{className:`form-label`,children:[`Allowed routes `,u.size===0&&(0,F.jsx)(`span`,{className:`hint`,children:`(all routes)`})]}),(0,F.jsxs)(`div`,{className:`route-scope-list`,children:[m?.routes.map(e=>(0,F.jsxs)(`label`,{className:`route-scope-item`,children:[(0,F.jsx)(`input`,{type:`checkbox`,name:`route-${e.id}`,checked:u.has(e.id),onChange:()=>{d(t=>{let n=new Set(t);return n.has(e.id)?n.delete(e.id):n.add(e.id),n})}}),(0,F.jsx)(`span`,{children:e.name})]},e.id)),!m?.routes.length&&(0,F.jsx)(`span`,{className:`hint`,children:`No routes configured`})]})]})]}),(0,F.jsx)(nc,{open:f,onClose:()=>p(!1),onConfirm:g,title:`Revoke key?`,message:(0,F.jsxs)(F.Fragment,{children:[`Revoking `,(0,F.jsxs)(`span`,{className:`mono`,children:[e.key_prefix,`…`]}),` will immediately reject any request using it. This cannot be undone.`]}),confirmLabel:`Revoke`})]})}function uz(){let e=La(),[t,n]=Bi(),r=t.get(`q`)??``,i=t.get(`edit`),[a,o]=(0,P.useState)(null),[s,c]=(0,P.useState)(null);(0,P.useEffect)(()=>{if(!i||!e.data)return;let t=e.data.find(e=>String(e.id)===i);t&&c(t)},[i,e.data]);function l(){if(c(null),t.has(`edit`)){let e=new URLSearchParams(t);e.delete(`edit`),n(e,{replace:!0})}}function u(e){c(e);let r=new URLSearchParams(t);r.set(`edit`,String(e.id)),n(r,{replace:!0})}function d(e){let r=new URLSearchParams(t);e?r.set(`q`,e):r.delete(`q`),n(r,{replace:!0})}let f=r.trim().toLowerCase(),p=(0,P.useMemo)(()=>f?(e.data??[]).filter(e=>{let t=(e.description??``).toLowerCase(),n=e.key_prefix.toLowerCase();return t.includes(f)||n.includes(f)}):e.data??[],[e.data,f]);return(0,F.jsxs)(`div`,{children:[(0,F.jsx)(cz,{onCreated:o}),a&&(0,F.jsxs)(`div`,{className:`key-result`,children:[(0,F.jsx)(`div`,{className:`key-result-label`,children:`New key (copy now — not shown again)`}),a]}),(0,F.jsxs)(`div`,{className:`toolbar`,children:[(0,F.jsx)(`input`,{type:`search`,name:`keys-search`,placeholder:`Search by description or prefix…`,value:r,onChange:e=>d(e.target.value),className:`toolbar-search`}),e.data&&(0,F.jsxs)(`span`,{className:`dim toolbar-count`,children:[p.length,` of `,e.data.length]})]}),(0,F.jsx)(Zs,{query:e,errorTitle:`Failed to load keys`,empty:{when:e=>e.length===0,render:()=>(0,F.jsxs)(`div`,{className:`empty-cta`,children:[(0,F.jsx)(`div`,{className:`empty-cta-title`,children:`No virtual keys yet`}),(0,F.jsx)(`div`,{className:`empty-cta-body`,children:`Use the form above to create one. Keys are shown once at creation and hashed in storage.`})]})},children:()=>p.length===0?(0,F.jsxs)(`div`,{className:`empty`,children:[`No keys match "`,r,`".`]}):(0,F.jsxs)(`table`,{className:`keys-grid`,children:[(0,F.jsx)(`thead`,{children:(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`th`,{children:`Prefix`}),(0,F.jsx)(`th`,{children:`Description`}),(0,F.jsx)(`th`,{children:`Status`}),(0,F.jsx)(`th`,{children:`Spend`}),(0,F.jsx)(`th`,{children:`Requests`}),(0,F.jsx)(`th`,{children:`Created`})]})}),(0,F.jsx)(`tbody`,{children:p.map(e=>(0,F.jsxs)(`tr`,{style:{cursor:`pointer`},onClick:()=>u(e),children:[(0,F.jsxs)(`td`,{className:`mono`,children:[e.key_prefix,`…`]}),(0,F.jsx)(`td`,{className:`dim`,children:e.description??`—`}),(0,F.jsx)(`td`,{children:(0,F.jsx)(oz,{variant:e.status})}),(0,F.jsx)(`td`,{children:(0,F.jsx)(sz,{spent:e.total_spend,limit:e.spend_limit})}),(0,F.jsx)(`td`,{className:`mono`,children:e.total_requests.toLocaleString()}),(0,F.jsx)(`td`,{className:`mono dim`,children:e.created_at.slice(0,10)})]},e.id))})]})}),s&&(0,F.jsx)(lz,{vk:s,onClose:l},s.id)]})}var dz={openrouter:{text:`Public, no key needed`,needsKey:!1},deepinfra:{text:`Public, no key needed`,needsKey:!1},ollama:{text:`No key needed (local)`,needsKey:!1},configured:{text:`API key required`,needsKey:!0},custom:{text:`API key may be required`,needsKey:!0}};function fz(){return(0,F.jsx)(`svg`,{width:`12`,height:`12`,viewBox:`0 0 16 16`,fill:`none`,className:`key-icon-inline`,children:(0,F.jsx)(`path`,{d:`M10.5 1a4.5 4.5 0 0 0-4.1 6.35L2 11.75V15h3.25v-2H7v-1.75h1.75L9.65 10.4A4.5 4.5 0 1 0 10.5 1zm1 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2z`,fill:`currentColor`})})}function pz(){let e=Ka(),t=qa(),n=Ja(),r=Ya(),{data:i}=Va(),{data:a}=to(),[o,s]=(0,P.useState)(``),[c,l]=(0,P.useState)(``),[u,d]=(0,P.useState)(``),[f,p]=(0,P.useState)(`openrouter`),[m,h]=(0,P.useState)(``),[g,_]=(0,P.useState)(``),[v,y]=(0,P.useState)(null),b=dz[f]??dz.custom;function x(){r.mutate({source:f,...f===`custom`?{url:m}:{}})}function S(){return v?n.mutateAsync(v).then(()=>void 0):Promise.resolve()}let C=g.trim().toLowerCase(),w=(0,P.useMemo)(()=>{let t=e.data?.models??[];return C?t.filter(e=>e.model_name.toLowerCase().includes(C)):t},[e.data,C]);return(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`div`,{className:`models-discover`,children:[(0,F.jsx)(`div`,{className:`section-label`,children:`Discover Models`}),(0,F.jsxs)(`div`,{className:`models-discover-row`,children:[(0,F.jsxs)(`select`,{value:f,onChange:e=>{p(e.target.value),r.reset()},children:[(0,F.jsx)(`option`,{value:`openrouter`,children:`OpenRouter`}),(0,F.jsx)(`option`,{value:`deepinfra`,children:`DeepInfra`}),(0,F.jsx)(`option`,{value:`ollama`,children:`Ollama (local)`}),(0,F.jsx)(`option`,{value:`configured`,children:`Configured backend`}),(0,F.jsx)(`option`,{value:`custom`,children:`Custom URL`})]}),f===`custom`&&(0,F.jsx)(`input`,{name:`discover-url`,placeholder:`https://api.example.com`,value:m,onChange:e=>h(e.target.value),style:{minWidth:220}}),(0,F.jsx)(V,{onClick:x,disabled:r.isPending||f===`custom`&&!m,loading:r.isPending,children:`Fetch`}),(0,F.jsxs)(`span`,{className:`dim models-discover-hint`,children:[b.needsKey&&(0,F.jsx)(fz,{}),b.text]})]}),r.isError&&(0,F.jsx)(`div`,{className:`inline-error`,children:r.error.message}),r.data&&r.data.models.length>0&&(0,F.jsxs)(`div`,{className:`models-discover-results`,children:[(0,F.jsxs)(`div`,{className:`dim models-discover-count`,children:[r.data.models.length,` model`,r.data.models.length===1?``:`s`,` found. Click to populate the form below.`]}),(0,F.jsx)(`div`,{className:`models-discover-list`,children:r.data.models.map(e=>(0,F.jsxs)(`div`,{onClick:()=>l(e.id),className:`models-discover-item${c===e.id?` is-selected`:``}`,children:[(0,F.jsx)(`span`,{className:`mono`,children:e.id}),e.name&&e.name!==e.id&&(0,F.jsx)(`span`,{className:`dim models-discover-item-name`,children:e.name})]},e.id))})]}),r.data&&r.data.models.length===0&&(0,F.jsx)(`div`,{className:`dim models-discover-count`,children:`No models returned.`})]}),(0,F.jsxs)(`datalist`,{id:`backends-list`,children:[i?.map(e=>(0,F.jsx)(`option`,{value:e.name,children:e.name},e.name)),a?.backends.map(e=>(0,F.jsxs)(`option`,{value:e.name,children:[e.name,` (managed)`]},`managed-${e.name}`))]}),(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsx)(`div`,{className:`form-label`,children:`Add Model`}),(0,F.jsxs)(`div`,{className:`form-row`,style:{flexWrap:`wrap`},children:[(0,F.jsx)(`input`,{name:`model-name`,placeholder:`Virtual name`,value:o,onChange:e=>s(e.target.value)}),(0,F.jsx)(`input`,{name:`model-id`,placeholder:`Model ID`,value:c,onChange:e=>l(e.target.value)}),(0,F.jsx)(`input`,{name:`backend`,placeholder:`Backend`,value:u,onChange:e=>d(e.target.value),list:`backends-list`}),(0,F.jsx)(V,{tone:`primary`,onClick:()=>t.mutate({model_name:o,actual_model:c,backend_name:u}),disabled:!o||!c||!u||t.isPending,loading:t.isPending,children:`Add`})]})]}),(0,F.jsxs)(`div`,{className:`toolbar`,children:[(0,F.jsx)(`input`,{type:`search`,name:`models-search`,placeholder:`Search models…`,value:g,onChange:e=>_(e.target.value),className:`toolbar-search`}),e.data&&(0,F.jsxs)(`span`,{className:`dim toolbar-count`,children:[w.length,` of `,e.data.models.length]})]}),(0,F.jsx)(Zs,{query:e,errorTitle:`Failed to load models`,empty:{when:e=>(e.models?.length??0)===0,render:()=>(0,F.jsxs)(H,{className:`empty-cta`,children:[(0,F.jsx)(`div`,{className:`empty-cta-title`,children:`No models configured`}),(0,F.jsx)(`div`,{className:`empty-cta-body`,children:`Add a model above, or use Discover to pull a catalog from OpenRouter, DeepInfra, Ollama, or a custom endpoint.`})]})},children:e=>w.length===0?(0,F.jsxs)(`div`,{className:`empty`,children:[`No models match "`,g,`".`]}):(0,F.jsxs)(`table`,{className:`route-table`,children:[(0,F.jsx)(`thead`,{children:(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`th`,{children:`Virtual Name`}),(0,F.jsx)(`th`,{children:`Deployments`}),(0,F.jsx)(`th`,{children:`Strategy`}),(0,F.jsx)(`th`,{})]})}),(0,F.jsx)(`tbody`,{children:w.map(t=>(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`td`,{className:`mono`,children:t.model_name}),(0,F.jsx)(`td`,{className:`mono`,children:t.deployments}),(0,F.jsx)(`td`,{className:`dim`,children:e.strategy??`—`}),(0,F.jsx)(`td`,{children:(0,F.jsx)(V,{tone:`danger`,size:`sm`,onClick:()=>y(t.model_name),children:`Remove`})})]},t.model_name))})]})}),(0,F.jsx)(nc,{open:v!==null,onClose:()=>y(null),onConfirm:S,title:`Remove model?`,message:(0,F.jsxs)(F.Fragment,{children:[`Remove model `,(0,F.jsx)(`span`,{className:`mono`,children:v}),`? Requests using this virtual name will fail until another model with the same name is added.`]}),confirmLabel:`Remove`})]})}function mz(){let[e,t]=Bi(),n=Math.max(1,Number(e.get(`page`)??`1`)||1);function r(r){let i=new URLSearchParams(e),a=r(n);a<=1?i.delete(`page`):i.set(`page`,String(a)),t(i,{replace:!0})}return(0,F.jsx)(`div`,{children:(0,F.jsx)(Zs,{query:Xa({page:n,page_size:50}),errorTitle:`Failed to load audit log`,empty:{when:e=>e.entries.length===0&&n===1,render:()=>(0,F.jsxs)(`div`,{className:`empty-cta`,children:[(0,F.jsx)(`div`,{className:`empty-cta-title`,children:`No audit entries yet`}),(0,F.jsx)(`div`,{className:`empty-cta-body`,children:`Admin actions (creating keys, editing routes, managing backends) are recorded here.`})]})},children:e=>(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`table`,{className:`route-table`,children:[(0,F.jsx)(`thead`,{children:(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`th`,{children:`Time`}),(0,F.jsx)(`th`,{children:`Action`}),(0,F.jsx)(`th`,{children:`Target`}),(0,F.jsx)(`th`,{children:`Detail`}),(0,F.jsx)(`th`,{children:`IP`})]})}),(0,F.jsx)(`tbody`,{children:e.entries.map(e=>(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`td`,{className:`mono dim`,children:e.timestamp.slice(0,19)}),(0,F.jsx)(`td`,{className:`mono`,children:e.action}),(0,F.jsxs)(`td`,{className:`dim`,children:[e.target_type,e.target_id?` #${e.target_id}`:``]}),(0,F.jsx)(`td`,{className:`dim audit-detail`,children:e.detail??`—`}),(0,F.jsx)(`td`,{className:`mono dim`,children:e.source_ip??`—`})]},e.id))})]}),(0,F.jsx)(Xs,{page:n,hasMore:e.has_more,onPrev:()=>r(e=>Math.max(1,e-1)),onNext:()=>r(e=>e+1)})]})})})}function hz({routes:e}){let t=[...e].sort((e,t)=>t.requests_per_min-e.requests_per_min);return(0,F.jsxs)(`table`,{className:`route-table`,children:[(0,F.jsx)(`thead`,{children:(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`th`,{children:`Route`}),(0,F.jsx)(`th`,{children:`Req/min`}),(0,F.jsx)(`th`,{children:`Error rate`}),(0,F.jsx)(`th`,{children:`Avg latency`}),(0,F.jsx)(`th`,{children:`P95 latency`}),(0,F.jsx)(`th`,{children:`Total`})]})}),(0,F.jsx)(`tbody`,{children:t.map(e=>(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`td`,{className:`mono`,children:e.path}),(0,F.jsx)(`td`,{className:`mono`,children:e.requests_per_min.toFixed(2)}),(0,F.jsxs)(`td`,{className:`mono`,style:{color:e.error_rate>.05?`var(--err)`:e.error_rate>.01?`var(--warn)`:void 0},children:[(e.error_rate*100).toFixed(1),`%`]}),(0,F.jsxs)(`td`,{className:`mono`,children:[e.avg_latency_ms.toFixed(0),`ms`]}),(0,F.jsxs)(`td`,{className:`mono`,children:[e.p95_latency_ms,`ms`]}),(0,F.jsx)(`td`,{className:`mono`,children:e.total_requests.toLocaleString()})]},e.path))})]})}var gz=[`#e8a030`,`#d4922b`,`#c07820`,`#a86015`,`#8c500a`],_z=[`#6eb5c0`,`#5aa0ab`,`#468b96`,`#327681`,`#1e616c`];function vz(){let[e,t]=(0,P.useState)(6),{data:n,isLoading:r,error:i}=Za(e),a=n?.routes??[],o=a.slice(0,5).map((e,t)=>{let r=(n?.series??[]).filter(t=>t.path===e.path).map(e=>e.requests);return{label:e.path,color:gz[t%gz.length],data:r}});return(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`div`,{className:`section-header`,children:[(0,F.jsx)(`span`,{className:`section-label`,children:`Traffic`}),(0,F.jsxs)(`select`,{value:e,onChange:e=>t(Number(e.target.value)),children:[(0,F.jsx)(`option`,{value:1,children:`Last 1 hour`}),(0,F.jsx)(`option`,{value:6,children:`Last 6 hours`}),(0,F.jsx)(`option`,{value:24,children:`Last 24 hours`})]})]}),(0,F.jsx)(qs,{loading:r,error:i?.message}),n&&(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(hz,{routes:n.routes}),(0,F.jsxs)(`div`,{className:`operator-grid`,style:{marginTop:16},children:[(0,F.jsxs)(H,{className:`chart-card`,children:[(0,F.jsx)(`div`,{className:`chart-header`,children:(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`div`,{className:`chart-title`,children:`Requests / min by route`}),(0,F.jsx)(`div`,{className:`chart-subtitle`,children:`Stacked over time window`})]})}),(0,F.jsx)(Ks,{series:o})]}),(0,F.jsxs)(H,{className:`chart-card`,children:[(0,F.jsx)(`div`,{className:`chart-header`,children:(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`div`,{className:`chart-title`,children:`Avg latency per route`}),(0,F.jsx)(`div`,{className:`chart-subtitle`,children:`ms`})]})}),a.length===0?(0,F.jsx)(`div`,{className:`empty`,children:`No routes`}):(0,F.jsx)(`div`,{style:{display:`flex`,flexDirection:`column`,gap:8,paddingTop:8},children:a.slice(0,5).map((e,t)=>{let n=Math.max(...a.slice(0,5).map(e=>e.avg_latency_ms),1),r=e.avg_latency_ms/n*100;return(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`div`,{style:{display:`flex`,justifyContent:`space-between`,fontSize:11,marginBottom:2},children:[(0,F.jsx)(`span`,{className:`mono dim`,style:{overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`,maxWidth:`70%`},children:e.path}),(0,F.jsxs)(`span`,{className:`mono`,children:[e.avg_latency_ms.toFixed(0),`ms`]})]}),(0,F.jsx)(`div`,{style:{height:6,background:`var(--border)`,borderRadius:0},children:(0,F.jsx)(`div`,{style:{height:`100%`,width:`${r}%`,background:_z[t%_z.length],borderRadius:0}})})]},e.path)})})]})]})]})]})}function yz(e){let t=Math.floor(Date.now()/1e3-e),n=Math.floor(t/86400),r=Math.floor(t%86400/3600),i=Math.floor(t%3600/60);return n>0?`${n}d ${r}h ${i}m`:r>0?`${r}h ${i}m`:`${i}m`}function bz({proxy:e}){return(0,F.jsxs)(`div`,{className:`uptime-proxy`,children:[(0,F.jsxs)(`div`,{className:`uptime-proxy-stats`,children:[(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`div`,{className:`section-label`,children:`Uptime (30d)`}),(0,F.jsxs)(`div`,{className:`uptime-pct`,children:[e.uptime_pct_30d.toFixed(2),`%`]})]}),(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`div`,{className:`section-label`,children:`Running`}),(0,F.jsx)(`div`,{className:`stat-value`,style:{fontSize:16},children:yz(e.started_at)})]})]}),(0,F.jsx)(`div`,{className:`section-label`,style:{marginBottom:4},children:`30-day history`}),(0,F.jsx)(`div`,{className:`history-bar`,children:e.history.map(e=>(0,F.jsx)(`div`,{className:`history-day ${e.status}`,title:`${e.date}: ${e.status}`},e.date))})]})}var xz=Ns;function Sz({b:e}){let t=e.status===`up`?`ok`:e.status===`down`?`err`:`dim`,n=e.last_checked_at?new Date(e.last_checked_at*1e3).toLocaleTimeString():`—`;return(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`td`,{className:`mono`,children:e.name}),(0,F.jsxs)(`td`,{children:[(0,F.jsx)(xz,{status:t,pulse:e.status===`up`}),e.status]}),(0,F.jsxs)(`td`,{className:`mono`,children:[e.uptime_pct_30d.toFixed(2),`%`]}),(0,F.jsx)(`td`,{className:`mono dim`,children:n}),(0,F.jsx)(`td`,{className:`mono dim`,children:e.last_latency_ms==null?`—`:`${e.last_latency_ms}ms`}),(0,F.jsx)(`td`,{children:(0,F.jsx)(`div`,{className:`history-bar`,style:{height:12},children:e.history.map(e=>(0,F.jsx)(`div`,{className:`history-day ${e.status}`,title:`${e.date}: ${e.status}`},e.date))})})]})}function Cz(){let{data:e,isLoading:t,error:n}=Qa();return(0,F.jsxs)(`div`,{children:[(0,F.jsx)(qs,{loading:t,error:n?.message}),e&&(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(bz,{proxy:e.proxy}),(0,F.jsx)(`div`,{className:`section-label`,style:{marginTop:16,marginBottom:8},children:`Backend Availability`}),(0,F.jsxs)(`table`,{className:`backend-health-table`,children:[(0,F.jsx)(`thead`,{children:(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`th`,{children:`Backend`}),(0,F.jsx)(`th`,{children:`Status`}),(0,F.jsx)(`th`,{children:`Uptime (30d)`}),(0,F.jsx)(`th`,{children:`Last checked`}),(0,F.jsx)(`th`,{children:`Latency`}),(0,F.jsx)(`th`,{children:`History`})]})}),(0,F.jsx)(`tbody`,{children:e.backends.slice().sort((e,t)=>e.name.localeCompare(t.name)).map(e=>(0,F.jsx)(Sz,{b:e},e.name))})]})]})]})}var wz={openai:0,anthropic:0,gemini:0,vertex:0,azure:1,bedrock:1,mistral:1,groq:1,deepseek:1,xai:1,together_ai:2,openrouter:2,fireworks_ai:2,perplexity:2,cohere_chat:2,cerebras:2,sambanova:2,ollama:2,deepinfra:2,replicate:2,nvidia_nim:2},Tz={0:`Top providers`,1:`Popular`,2:`Notable`,3:`More providers`};function Ez(e){let t=new Map;for(let n of e){let e=wz[n.id]??3;t.has(e)||t.set(e,[]),t.get(e).push(n)}for(let e of t.values())e.sort((e,t)=>e.display_name.localeCompare(t.display_name));return[...t.entries()].sort(([e],[t])=>e-t).map(([e,t])=>({tier:e,label:Tz[e]??`Other`,providers:t}))}function Dz({provider:e,backendCount:t,onClick:n}){return(0,F.jsxs)(`button`,{type:`button`,className:`provider-tile${t>0?` has-backends`:``}`,onClick:n,children:[(0,F.jsx)($R,{id:e.id,size:28}),(0,F.jsx)(`span`,{className:`provider-tile-name`,children:e.display_name}),t>0&&(0,F.jsxs)(`span`,{className:`provider-tile-count`,children:[t,` key`,t===1?``:`s`]})]})}function Oz({backend:e,healthStatus:t,onDelete:n}){return(0,F.jsxs)(`div`,{className:`provider-backend-row`,children:[(0,F.jsx)(xz,{status:t===`up`?`ok`:t===`down`?`err`:`dim`,pulse:t===`up`}),(0,F.jsx)(`span`,{className:`backend-name`,children:e.name}),(0,F.jsxs)(`span`,{className:`backend-status`,children:[e.api_key_set?`key set`:`no key`,e.rpm!=null&&(0,F.jsxs)(F.Fragment,{children:[` · RPM `,e.rpm]})]}),(0,F.jsx)(V,{tone:`danger`,size:`sm`,onClick:n,children:`Delete`})]})}function kz({provider:e,existingCount:t,onCreated:n}){let r=no(),[i,a]=(0,P.useState)(!1),[o,s]=(0,P.useState)(()=>({name:`${e.id}-${t+1}`,provider_id:e.id}));(0,P.useEffect)(()=>{s({name:`${e.id}-${t+1}`,provider_id:e.id}),a(!1)},[e.id,t]);function c(){r.mutate({name:o.name,provider_id:e.id,api_key:o.api_key||void 0,api_base:o.api_base||void 0,deployment:o.deployment||void 0,api_version:o.api_version||void 0,project:o.project||void 0,region:o.region||void 0,aws_access_key_id:o.aws_access_key_id||void 0,aws_secret_access_key:o.aws_secret_access_key||void 0,aws_session_token:o.aws_session_token||void 0,rpm:o.rpm?Number(o.rpm):void 0,tpm:o.tpm?Number(o.tpm):void 0},{onSuccess:()=>{a(!1),n()}})}if(!i)return(0,F.jsx)(`div`,{className:`provider-add-toggle`,children:(0,F.jsx)(V,{tone:`primary`,size:`sm`,onClick:()=>a(!0),children:`+ Add key`})});let l=ez(e);return(0,F.jsxs)(`div`,{className:`provider-add-form`,children:[(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsx)(`label`,{className:`form-label`,htmlFor:`add-backend-name`,children:`Name`}),(0,F.jsx)(`input`,{id:`add-backend-name`,name:`name`,type:`text`,value:o.name,onChange:e=>s(t=>({...t,name:e.target.value})),style:{width:`100%`}})]}),l.map(e=>(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsx)(`label`,{className:`form-label`,htmlFor:`add-${e.name}`,children:e.label}),e.hint&&(0,F.jsx)(`div`,{className:`form-hint`,children:e.hint}),(0,F.jsx)(`input`,{id:`add-${e.name}`,name:e.name,type:e.type,placeholder:e.placeholder,value:o[e.name]??``,onChange:t=>s(n=>({...n,[e.name]:t.target.value})),style:{width:`100%`}})]},e.name)),r.isError&&(0,F.jsx)(`div`,{className:`inline-error`,children:`Failed to create backend`}),(0,F.jsxs)(`div`,{className:`provider-add-actions`,children:[(0,F.jsx)(V,{size:`sm`,onClick:()=>a(!1),disabled:r.isPending,children:`Cancel`}),(0,F.jsx)(V,{tone:`primary`,size:`sm`,onClick:c,disabled:!o.name||r.isPending,loading:r.isPending,children:`Create`})]})]})}function Az({provider:e,backends:t,healthMap:n,onClose:r,onDeleteBackend:i}){(0,P.useEffect)(()=>{function e(e){e.key===`Escape`&&r()}return document.addEventListener(`keydown`,e),()=>document.removeEventListener(`keydown`,e)},[r]),(0,P.useEffect)(()=>{let e=document.body.style.overflow;return document.body.style.overflow=`hidden`,()=>{document.body.style.overflow=e}},[]);let a=e.capabilities,o=[[`chat`,a.chat_completions],[`streaming`,a.streaming],[`tool use`,a.tool_use],[`vision`,a.vision],[`embeddings`,a.embeddings],[`batch`,a.batch]];return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`div`,{className:`provider-scrim`,onClick:r}),(0,F.jsxs)(H,{className:`provider-panel`,role:`dialog`,"aria-modal":`true`,"aria-label":e.display_name,children:[(0,F.jsxs)(`div`,{className:`provider-panel-header`,children:[(0,F.jsx)($R,{id:e.id,size:36}),(0,F.jsx)(`h3`,{children:e.display_name}),(0,F.jsx)(`button`,{type:`button`,className:`provider-panel-close`,onClick:r,"aria-label":`Close`,children:`×`})]}),(0,F.jsxs)(`div`,{className:`provider-panel-caps`,children:[o.map(([e,t])=>(0,F.jsx)(`span`,{className:`badge-cap${t?` active`:``}`,children:e},e)),(0,F.jsxs)(`span`,{style:{marginLeft:`auto`},className:`badge-cap active`,children:[e.model_count,` models`]})]}),(0,F.jsxs)(`div`,{className:`provider-panel-meta`,children:[(0,F.jsxs)(`span`,{children:[`Protocol: `,(0,F.jsx)(`span`,{className:`mono`,children:e.protocol.replace(/_/g,` `)})]}),(0,F.jsxs)(`span`,{children:[`Auth: `,(0,F.jsx)(`span`,{className:`mono`,children:e.auth.replace(/_/g,` `)})]}),(0,F.jsxs)(`span`,{children:[`Status: `,(0,F.jsx)(`span`,{className:`mono`,children:e.status})]}),e.env_vars.length>0&&(0,F.jsxs)(`span`,{children:[`Env: `,(0,F.jsx)(`span`,{className:`mono`,children:e.env_vars[0]})]})]}),(0,F.jsxs)(`div`,{className:`provider-panel-section`,children:[(0,F.jsxs)(`div`,{className:`provider-panel-section-label`,children:[`Configured keys (`,t.length,`)`]}),t.length===0&&(0,F.jsx)(`div`,{className:`provider-empty-hint`,children:`No keys configured. Add one below to start forwarding requests.`}),t.map(e=>(0,F.jsx)(Oz,{backend:e,healthStatus:n.get(e.name),onDelete:()=>i(e)},e.id)),(0,F.jsx)(kz,{provider:e,existingCount:t.length,onCreated:()=>{}})]})]})]})}function jz(){let e=eo(),t=to(),{data:n}=Qa(),r=io(),[i,a]=(0,P.useState)(null),[o,s]=(0,P.useState)(``),[c,l]=(0,P.useState)(null),u=(0,P.useMemo)(()=>e.data??[],[e.data]),d=(0,P.useMemo)(()=>t.data?.backends??[],[t.data]),f=(0,P.useMemo)(()=>{let e=new Map;for(let t of d)e.has(t.provider_id)||e.set(t.provider_id,[]),e.get(t.provider_id).push(t);return e},[d]),p=(0,P.useMemo)(()=>{let e=new Map;for(let t of n?.backends??[])e.set(t.name,t.status);return e},[n]),m=(0,P.useMemo)(()=>{let e=o.toLowerCase();return Ez(e?u.filter(t=>t.display_name.toLowerCase().includes(e)||t.id.includes(e)):u)},[u,o]),h=i?u.find(e=>e.id===i):null,g=i?f.get(i)??[]:[];function _(){return c?r.mutateAsync(c.name).then(()=>void 0):Promise.resolve()}return(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`div`,{className:`section-header`,children:[(0,F.jsx)(`h2`,{children:`Providers`}),(0,F.jsx)(`input`,{type:`search`,name:`provider-search`,placeholder:`Search providers...`,value:o,onChange:e=>s(e.target.value),style:{width:260}})]}),(0,F.jsx)(Zs,{query:e,errorTitle:`Failed to load provider catalog`,empty:{when:()=>u.length===0,render:()=>(0,F.jsxs)(H,{className:`empty-cta`,children:[(0,F.jsx)(`div`,{className:`empty-cta-title`,children:`No providers available`}),(0,F.jsx)(`div`,{className:`empty-cta-body`,children:`The provider catalog is empty. Check that the providers crate is loaded.`})]})},children:()=>(0,F.jsxs)(`div`,{className:`provider-catalog`,children:[m.map(e=>(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`div`,{className:`provider-tier-label`,children:e.label}),(0,F.jsx)(`div`,{className:`provider-tile-grid${e.tier===0?` tier-top`:``}`,children:e.providers.map(e=>(0,F.jsx)(Dz,{provider:e,backendCount:f.get(e.id)?.length??0,onClick:()=>a(e.id)},e.id))})]},e.tier)),m.length===0&&o&&(0,F.jsxs)(`div`,{className:`dim`,style:{padding:20},children:[`No providers match "`,o,`".`]})]})}),h&&(0,F.jsx)(Az,{provider:h,backends:g,healthMap:p,onClose:()=>a(null),onDeleteBackend:l},i),(0,F.jsx)(nc,{open:c!==null,onClose:()=>l(null),onConfirm:_,title:`Delete backend?`,message:(0,F.jsxs)(F.Fragment,{children:[`Delete backend `,(0,F.jsx)(`span`,{className:`mono`,children:c?.name}),`? Routes referencing this backend will lose it from their provider list.`]})})]})}function Mz(){let{data:e,isLoading:t,error:n}=Va(),{data:r}=Qa(),i=(0,P.useMemo)(()=>{let e=new Map;for(let t of r?.backends??[])e.set(t.name,t);return e},[r]);return(0,F.jsxs)(`div`,{children:[(0,F.jsx)(qs,{loading:t,error:n?.message,empty:e?.length===0}),(0,F.jsx)(`div`,{className:`backend-cards`,children:e?.map(e=>{let t=i.get(e.name),n=t?.status===`up`?`ok`:t?.status===`down`?`err`:`dim`;return(0,F.jsxs)(H,{className:`card`,glowOnHover:!0,children:[(0,F.jsxs)(`div`,{className:`card-header`,children:[(0,F.jsx)(`span`,{className:`card-name`,children:e.name}),(0,F.jsx)(xz,{status:n,pulse:n===`ok`})]}),(0,F.jsxs)(`div`,{className:`card-body`,children:[(0,F.jsxs)(`div`,{className:`mono`,children:[e.big_model,` / `,e.small_model]}),(0,F.jsxs)(`div`,{style:{marginTop:6,display:`grid`,gridTemplateColumns:`1fr 1fr`,gap:4},children:[(0,F.jsx)(`span`,{className:`dim`,children:`Requests`}),(0,F.jsx)(`span`,{className:`mono`,children:e.metrics.requests_total}),(0,F.jsx)(`span`,{className:`dim`,children:`Errors`}),(0,F.jsx)(`span`,{className:`mono`,style:{color:e.metrics.requests_error>0?`var(--err)`:void 0},children:e.metrics.requests_error}),t?.last_latency_ms!=null&&(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`span`,{className:`dim`,children:`Last latency`}),(0,F.jsxs)(`span`,{className:`mono`,children:[t.last_latency_ms,`ms`]})]}),t&&(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`span`,{className:`dim`,children:`30d uptime`}),(0,F.jsxs)(`span`,{className:`mono`,children:[t.uptime_pct_30d,`%`]})]})]})]})]},e.name)})})]})}function Nz({route:e,onClose:t}){let{data:n,isLoading:r}=co(e.id),i=lo(),a=uo(),o=fo(),s=po(),{data:c}=to(),[l,u]=(0,P.useState)(!1),[d,f]=(0,P.useState)(``),[p,m]=(0,P.useState)(`*`),h=n?.providers??[],g=c?.backends??[],_=new Set(h.map(e=>e.backend_id)),v=g.filter(e=>!_.has(e.id));function y(){if(!d)return;let t=p.trim()===`*`?[`*`]:p.split(`,`).map(e=>e.trim()).filter(Boolean);i.mutate({routeId:e.id,data:{backend_id:d,models:t,priority:h.length,enabled:!0}},{onSuccess:()=>{u(!1),f(``),m(`*`)}})}function b(t,n){let r=t+n;if(r<0||r>=h.length)return;let i=h.slice(),[a]=i.splice(t,1);i.splice(r,0,a),s.mutate({routeId:e.id,data:{provider_ids:i.map(e=>e.id)}})}return(0,F.jsxs)(`div`,{className:`route-detail`,children:[(0,F.jsxs)(`div`,{className:`route-detail-header`,children:[(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`span`,{className:`route-detail-title`,children:e.name}),e.description&&(0,F.jsx)(`span`,{className:`dim route-detail-desc`,children:e.description})]}),(0,F.jsxs)(`div`,{className:`route-detail-meta`,children:[(0,F.jsxs)(`span`,{className:`dim`,children:[`strategy: `,e.strategy]}),e.rpm&&(0,F.jsxs)(`span`,{className:`dim mono`,children:[`RPM `,e.rpm]}),(0,F.jsx)(V,{size:`sm`,onClick:t,children:`Close`})]})]}),(0,F.jsxs)(`div`,{className:`route-detail-subhead`,children:[(0,F.jsx)(`span`,{className:`section-label route-detail-subhead-label`,children:`Providers (priority order)`}),(0,F.jsx)(V,{tone:`primary`,size:`sm`,onClick:()=>u(!l),children:l?`Cancel`:`+ Add Provider`})]}),l&&(0,F.jsxs)(`div`,{className:`route-detail-add`,children:[(0,F.jsxs)(`select`,{value:d,onChange:e=>f(e.target.value),className:`route-detail-add-select`,children:[(0,F.jsx)(`option`,{value:``,children:`Select provider...`}),v.map(e=>(0,F.jsxs)(`option`,{value:e.id,children:[e.name,` (`,e.provider_id,`)`]},e.id))]}),(0,F.jsx)(`input`,{type:`text`,name:`route-provider-models`,placeholder:`models (* for all)`,value:p,onChange:e=>m(e.target.value),className:`route-detail-add-models`}),(0,F.jsx)(V,{tone:`primary`,size:`sm`,onClick:y,disabled:!d||i.isPending,loading:i.isPending,children:`Add`})]}),r&&(0,F.jsx)(`div`,{className:`dim`,children:(0,F.jsx)(Ps,{label:`Loading providers`})}),!r&&h.length===0&&(0,F.jsx)(`div`,{className:`dim route-detail-empty`,children:`No providers assigned. Click "+ Add Provider" above.`}),!r&&h.map((t,n)=>(0,F.jsxs)(`div`,{className:`route-provider-row`,children:[(0,F.jsxs)(`span`,{className:`dim mono`,children:[n+1,`.`]}),(0,F.jsxs)(`span`,{children:[(0,F.jsx)(`span`,{className:`route-provider-name`,children:t.backend_name}),(0,F.jsxs)(`span`,{className:`dim route-provider-id`,children:[`(`,t.provider_id,`)`]})]}),(0,F.jsxs)(`span`,{className:`mono dim route-provider-models`,children:[`[`,t.models.join(`, `),`]`]}),(0,F.jsxs)(`span`,{className:`route-provider-reorder`,children:[(0,F.jsx)(V,{tone:`icon`,size:`sm`,className:`btn-icon`,onClick:()=>b(n,-1),disabled:n===0||s.isPending,"aria-label":`Move up`,children:`↑`}),(0,F.jsx)(V,{tone:`icon`,size:`sm`,className:`btn-icon`,onClick:()=>b(n,1),disabled:n>=h.length-1||s.isPending,"aria-label":`Move down`,children:`↓`})]}),(0,F.jsx)(V,{size:`sm`,tone:t.enabled?`primary`:`secondary`,className:`route-provider-toggle`,onClick:()=>a.mutate({routeId:e.id,providerId:t.id,data:{enabled:!t.enabled}}),children:t.enabled?`enabled`:`disabled`}),(0,F.jsx)(V,{tone:`danger`,size:`sm`,className:`route-provider-remove`,onClick:()=>o.mutate({routeId:e.id,providerId:t.id}),children:`Remove`})]},t.id))]})}function Pz({onClose:e}){let t=oo(),[n,r]=(0,P.useState)(``),[i,a]=(0,P.useState)(``);function o(){t.mutate({name:n,description:i||void 0},{onSuccess:e})}return(0,F.jsxs)(tc,{open:!0,onClose:e,title:`New Route`,size:`sm`,dismissable:!t.isPending,footer:(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(V,{onClick:e,disabled:t.isPending,children:`Cancel`}),(0,F.jsx)(V,{tone:`primary`,onClick:o,disabled:!n.trim()||t.isPending,loading:t.isPending,children:`Create`})]}),children:[(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsx)(`label`,{className:`form-label`,htmlFor:`route-name`,children:`Name`}),(0,F.jsx)(`input`,{id:`route-name`,name:`name`,type:`text`,value:n,onChange:e=>r(e.target.value),placeholder:`e.g. default, cheap`,style:{width:`100%`}})]}),(0,F.jsxs)(`div`,{className:`form-group`,children:[(0,F.jsx)(`label`,{className:`form-label`,htmlFor:`route-desc`,children:`Description`}),(0,F.jsx)(`input`,{id:`route-desc`,name:`description`,type:`text`,value:i,onChange:e=>a(e.target.value),placeholder:`optional`,style:{width:`100%`}})]}),t.isError&&(0,F.jsx)(`div`,{className:`error`,children:`Failed to create route`})]})}function Fz(){let e=ao(),t=so(),[n,r]=(0,P.useState)(null),[i,a]=(0,P.useState)(!1),[o,s]=(0,P.useState)(null);function c(){return o?t.mutateAsync(o.id).then(()=>void 0):Promise.resolve()}return(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`div`,{className:`section-header`,children:[(0,F.jsx)(`h2`,{children:`Routes`}),(0,F.jsx)(V,{tone:`primary`,onClick:()=>a(!0),children:`+ New Route`})]}),(0,F.jsx)(Zs,{query:e,errorTitle:`Failed to load routes`,empty:{when:e=>(e.routes?.length??0)===0,render:()=>(0,F.jsxs)(H,{className:`empty-cta`,children:[(0,F.jsx)(`div`,{className:`empty-cta-title`,children:`No routes yet`}),(0,F.jsx)(`div`,{className:`empty-cta-body`,children:`Create a route to fan requests out across multiple backends with priority-based failover.`}),(0,F.jsx)(V,{tone:`primary`,onClick:()=>a(!0),children:`+ New Route`})]})},children:e=>(0,F.jsxs)(`table`,{className:`route-table`,children:[(0,F.jsx)(`thead`,{children:(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`th`,{children:`Name`}),(0,F.jsx)(`th`,{children:`Strategy`}),(0,F.jsx)(`th`,{children:`Providers`}),(0,F.jsx)(`th`,{children:`Limits`}),(0,F.jsx)(`th`,{})]})}),(0,F.jsx)(`tbody`,{children:e.routes.map(e=>(0,F.jsx)(Iz,{route:e,expanded:n===e.id,onToggle:()=>r(n===e.id?null:e.id),onDelete:()=>s(e)},e.id))})]})}),i&&(0,F.jsx)(Pz,{onClose:()=>a(!1)}),(0,F.jsx)(nc,{open:o!==null,onClose:()=>s(null),onConfirm:c,title:`Delete route?`,message:(0,F.jsxs)(F.Fragment,{children:[`Delete route `,(0,F.jsx)(`span`,{className:`mono`,children:o?.name}),`? Virtual keys scoped to this route will lose access. This cannot be undone.`]})})]})}function Iz({route:e,expanded:t,onToggle:n,onDelete:r}){let i=[e.rpm&&`RPM ${e.rpm}`,e.tpm&&`TPM ${e.tpm}`].filter(Boolean).join(`, `)||`—`;return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`tr`,{className:`route-row`,onClick:n,children:[(0,F.jsxs)(`td`,{className:`route-row-name`,children:[t?`▾ `:`▸ `,e.name,e.description&&(0,F.jsx)(`span`,{className:`dim route-row-desc`,children:e.description})]}),(0,F.jsx)(`td`,{className:`dim`,children:e.strategy}),(0,F.jsx)(`td`,{children:e.provider_count}),(0,F.jsx)(`td`,{className:`mono dim`,children:i}),(0,F.jsx)(`td`,{className:`route-row-actions`,children:(0,F.jsx)(V,{tone:`danger`,size:`sm`,onClick:e=>{e.stopPropagation(),r()},children:`Delete`})})]}),t&&(0,F.jsx)(`tr`,{children:(0,F.jsx)(`td`,{colSpan:5,className:`route-row-detail-cell`,children:(0,F.jsx)(Nz,{route:e,onClose:n})})})]})}function Lz(){let e=Zi(e=>e.token),t=Zi(e=>e.login),n=Qi(e=>e.lastEvent),r=wn(),[i,a]=(0,P.useState)(!0),{data:o}=Na(!!e);if((0,P.useEffect)(()=>{let n=new URLSearchParams(location.search).get(`token`);n&&!e?fetch(`/admin/api/metrics`,{headers:{Authorization:`Bearer ${n}`}}).then(e=>{e.ok&&(t(n),history.replaceState(null,``,location.pathname))}).catch(()=>{}).finally(()=>a(!1)):a(!1)},[]),(0,P.useEffect)(()=>{e?aa():oa()},[e]),(0,P.useEffect)(()=>{n&&(n.type===`metrics_snapshot`?r.setQueryData([`metrics`],n.data):n.type===`backend_health_changed`&&r.invalidateQueries({queryKey:[`uptime`]}))},[n,r]),i)return null;if(!e)return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(Is,{}),(0,F.jsx)(zs,{})]});let s=o?.configured??!0;return(0,F.jsx)(Ni,{children:(0,F.jsxs)(`div`,{className:`app-layout`,children:[(0,F.jsx)(Rs,{}),(0,F.jsx)(`div`,{className:`tab-content`,children:(0,F.jsxs)(yi,{children:[(0,F.jsx)(_i,{path:`/`,element:(0,F.jsx)(gi,{to:s?`/dashboard`:`/settings`,replace:!0})}),(0,F.jsx)(_i,{path:`/dashboard`,element:(0,F.jsx)(Ys,{})}),(0,F.jsx)(_i,{path:`/requests`,element:(0,F.jsx)($s,{})}),(0,F.jsx)(_i,{path:`/traffic`,element:(0,F.jsx)(vz,{})}),(0,F.jsx)(_i,{path:`/providers`,element:(0,F.jsx)(jz,{})}),(0,F.jsx)(_i,{path:`/routes`,element:(0,F.jsx)(Fz,{})}),(0,F.jsx)(_i,{path:`/models`,element:(0,F.jsx)(pz,{})}),(0,F.jsx)(_i,{path:`/backends`,element:(0,F.jsx)(Mz,{})}),(0,F.jsx)(_i,{path:`/keys`,element:(0,F.jsx)(uz,{})}),(0,F.jsx)(_i,{path:`/audit`,element:(0,F.jsx)(mz,{})}),(0,F.jsx)(_i,{path:`/settings`,element:(0,F.jsx)(az,{configured:s})}),(0,F.jsx)(_i,{path:`/uptime`,element:(0,F.jsx)(Cz,{})}),(0,F.jsx)(_i,{path:`*`,element:(0,F.jsx)(gi,{to:`/dashboard`,replace:!0})})]})}),(0,F.jsx)(zs,{})]})})}var Rz=new bn({defaultOptions:{queries:{retry:(e,t)=>t instanceof xa?!1:e<1,refetchOnWindowFocus:!1,staleTime:3e4},mutations:{retry:(e,t)=>t instanceof xa?!1:e<1}}});(0,Hn.createRoot)(document.getElementById(`root`)).render((0,F.jsx)(P.StrictMode,{children:(0,F.jsx)(Tn,{client:Rz,children:(0,F.jsx)(Lz,{})})})); diff --git a/crates/proxy/admin-ui/package-lock.json b/crates/proxy/admin-ui/package-lock.json index fb4f5ff..c7820be 100644 --- a/crates/proxy/admin-ui/package-lock.json +++ b/crates/proxy/admin-ui/package-lock.json @@ -4997,9 +4997,9 @@ } }, "node_modules/dompurify": { - "version": "3.4.8", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.8.tgz", - "integrity": "sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ==", + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", + "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -6305,10 +6305,20 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1"