From 86180b191e25f459f30dbc54531be9abd11f9e9f Mon Sep 17 00:00:00 2001 From: Nathan Brake Date: Wed, 29 Jul 2026 18:31:14 +0000 Subject: [PATCH 1/4] feat(dashboard): surface failing requests so admins see dropped traffic Requests the gateway itself refused left no trace. The missing-pricing gate refunded the budget reservation and raised its 402 without writing a usage row, so an operator who turned require_pricing on had no way to see that live traffic was being dropped; they found out when a user complained. Every admin view was blind to it: the activity log, the overview error rate, and the recent-activity list all showed nothing. Log the rejection at both missing-pricing gates (the chat, messages, and responses pipeline, and the pass-through routes behind embeddings, images, and rerank) with status=error and cost=null. The reservation refund is unchanged, so nothing is billed and nothing stays reserved; the row exists to make the drop visible and countable. The pricing alarm then reads that count: "N requests failed in the last hour", plus a link into the activity log filtered to those failures, so the reject state reads as an active incident rather than a static config note. A new useFailureCount hook polls it through TanStack Query and resolves the window inside queryFn rather than in the query key, which keeps the key stable and re-anchors on every refetch, so a tab left open keeps reporting the last hour instead of widening to everything since it was opened. Every failure class is counted, not only the pricing rejections: the question in this state is whether traffic is getting through, and over-reporting a failure beats a banner reading "0" while requests are dropping. The activity detail panel's error summary no longer claims the provider returned the error. That was accurate while only provider failures were logged; gateway-side rejections land in that list now, so the copy is source-neutral. Provider diagnostics still stay server-side. Fixes #317 --- docs/dashboard.md | 3 + src/gateway/api/routes/_passthrough.py | 21 ++++- src/gateway/api/routes/_pipeline.py | 21 ++++- .../dashboard/assets/ActivityPage-D2jsRt9a.js | 1 + .../dashboard/assets/ActivityPage-DIACtZv5.js | 1 - ...ge-x0NFKgtH.js => AliasesPage-CrabMwlU.js} | 2 +- ...ge-D-S9-U_l.js => BudgetsPage-DjVks5Lb.js} | 2 +- ...-KvA7b1-a.js => ConfirmDialog-KQrh5qGO.js} | 2 +- ...sPage-BZcIfS8u.js => DocsPage-Bo_X9aMz.js} | 21 +++-- .../dashboard/assets/FilterChips-BbDynOdb.js | 1 + .../dashboard/assets/FilterChips-DZjxf-ot.js | 1 - ...sPage-CZjqxzex.js => KeysPage--hq_RPOL.js} | 6 +- ...lXgXz.js => ModelScopeControl-DYPdGezt.js} | 2 +- ...age-BkzSZx5b.js => ModelsPage-Dt9ZAYGy.js} | 2 +- .../dashboard/assets/OverviewPage-BedTU_aZ.js | 1 - .../dashboard/assets/OverviewPage-DPI1n-nt.js | 1 + .../assets/ProvidersPage-BP60GuZv.js | 1 - .../assets/ProvidersPage-CC2Glu9P.js | 1 + .../dashboard/assets/SettingsPage-CYDMBdEO.js | 1 + .../dashboard/assets/SettingsPage-DR0J2MHH.js | 1 - ...XhesdmH.js => TablePagination-BR55oZ8k.js} | 2 +- ...bOa.js => ToolsGuardrailsPage-B68S03Js.js} | 2 +- .../dashboard/assets/UsagePage-BYqtE3tC.js | 1 + .../dashboard/assets/UsagePage-Ce_jAzFl.js | 1 - ...Page-5kctrw19.js => UsersPage-CZXIBvdD.js} | 2 +- .../static/dashboard/assets/index-Br3lxdY5.js | 2 + .../static/dashboard/assets/index-DV8qvdUj.js | 2 - src/gateway/static/dashboard/index.html | 2 +- tests/integration/test_require_pricing.py | 44 ++++++++++ web/src/api/hooks.ts | 27 ++++++ web/src/components/PricingWarning.test.tsx | 84 ++++++++++++++++++- web/src/components/PricingWarning.tsx | 32 ++++++- web/src/pages/ActivityPage.test.tsx | 4 +- web/src/pages/ActivityPage.tsx | 7 +- 34 files changed, 266 insertions(+), 38 deletions(-) create mode 100644 src/gateway/static/dashboard/assets/ActivityPage-D2jsRt9a.js delete mode 100644 src/gateway/static/dashboard/assets/ActivityPage-DIACtZv5.js rename src/gateway/static/dashboard/assets/{AliasesPage-x0NFKgtH.js => AliasesPage-CrabMwlU.js} (82%) rename src/gateway/static/dashboard/assets/{BudgetsPage-D-S9-U_l.js => BudgetsPage-DjVks5Lb.js} (98%) rename src/gateway/static/dashboard/assets/{ConfirmDialog-KvA7b1-a.js => ConfirmDialog-KQrh5qGO.js} (92%) rename src/gateway/static/dashboard/assets/{DocsPage-BZcIfS8u.js => DocsPage-Bo_X9aMz.js} (95%) create mode 100644 src/gateway/static/dashboard/assets/FilterChips-BbDynOdb.js delete mode 100644 src/gateway/static/dashboard/assets/FilterChips-DZjxf-ot.js rename src/gateway/static/dashboard/assets/{KeysPage-CZjqxzex.js => KeysPage--hq_RPOL.js} (85%) rename src/gateway/static/dashboard/assets/{ModelScopeControl-u-XlXgXz.js => ModelScopeControl-DYPdGezt.js} (88%) rename src/gateway/static/dashboard/assets/{ModelsPage-BkzSZx5b.js => ModelsPage-Dt9ZAYGy.js} (86%) delete mode 100644 src/gateway/static/dashboard/assets/OverviewPage-BedTU_aZ.js create mode 100644 src/gateway/static/dashboard/assets/OverviewPage-DPI1n-nt.js delete mode 100644 src/gateway/static/dashboard/assets/ProvidersPage-BP60GuZv.js create mode 100644 src/gateway/static/dashboard/assets/ProvidersPage-CC2Glu9P.js create mode 100644 src/gateway/static/dashboard/assets/SettingsPage-CYDMBdEO.js delete mode 100644 src/gateway/static/dashboard/assets/SettingsPage-DR0J2MHH.js rename src/gateway/static/dashboard/assets/{TablePagination-CXhesdmH.js => TablePagination-BR55oZ8k.js} (98%) rename src/gateway/static/dashboard/assets/{ToolsGuardrailsPage-DiZ1TbOa.js => ToolsGuardrailsPage-B68S03Js.js} (92%) create mode 100644 src/gateway/static/dashboard/assets/UsagePage-BYqtE3tC.js delete mode 100644 src/gateway/static/dashboard/assets/UsagePage-Ce_jAzFl.js rename src/gateway/static/dashboard/assets/{UsersPage-5kctrw19.js => UsersPage-CZXIBvdD.js} (97%) create mode 100644 src/gateway/static/dashboard/assets/index-Br3lxdY5.js delete mode 100644 src/gateway/static/dashboard/assets/index-DV8qvdUj.js diff --git a/docs/dashboard.md b/docs/dashboard.md index 399d82e3..4f131e32 100644 --- a/docs/dashboard.md +++ b/docs/dashboard.md @@ -145,6 +145,9 @@ gateway. - **Activity**: the per-request log of what the gateway served, with filters. Use it to inspect individual requests, their models, and their outcomes. + Requests the gateway refused are logged too, so filtering to the `error` + status shows what is being dropped and why (for example a model with no + pricing under `require_pricing`). - **Usage**: aggregate usage and analytics, showing spend and volume over time, broken down by model and by user. diff --git a/src/gateway/api/routes/_passthrough.py b/src/gateway/api/routes/_passthrough.py index 91bc8f2b..ca9dd1c7 100644 --- a/src/gateway/api/routes/_passthrough.py +++ b/src/gateway/api/routes/_passthrough.py @@ -219,9 +219,28 @@ async def run_passthrough( and pricing_required_but_missing(pricing, require_pricing=config.require_pricing) ): await refund_reservation(db, reservation) + no_pricing_detail = no_pricing_error_detail(model) + # Record the rejection so dropped traffic is visible in the activity + # log and countable as an error, rather than only reaching the + # operator as a user complaint. cost stays null: nothing was spent. + await log_writer.put( + UsageLog( + id=str(uuid.uuid4()), + api_key_id=api_key_id, + user_id=user_id, + timestamp=datetime.now(UTC), + model=resolved.model, + provider=resolved.instance, + endpoint=endpoint, + status="error", + error_message=no_pricing_detail, + latency_ms=_elapsed_ms(started_at), + counts_toward_budget=not budget_exempt, + ) + ) raise HTTPException( status_code=status.HTTP_402_PAYMENT_REQUIRED, - detail=no_pricing_error_detail(model), + detail=no_pricing_detail, ) # Model access control (per-key). The reservation is already taken above (the diff --git a/src/gateway/api/routes/_pipeline.py b/src/gateway/api/routes/_pipeline.py index d4e52379..061bd542 100644 --- a/src/gateway/api/routes/_pipeline.py +++ b/src/gateway/api/routes/_pipeline.py @@ -710,9 +710,28 @@ async def resolve_request_context( # cost=null when unpriced. if not budget_exempt and pricing_required_but_missing(gate_pricing, require_pricing=config.require_pricing): await refund_reservation(db, reservation) + no_pricing_detail = no_pricing_error_detail(model) + # Record the rejection before raising. Gateway-side rejections used to + # leave no trace, so an operator who flipped require_pricing on had no + # way to see that live traffic was being dropped (only a user complaint + # would tell them). The row carries cost=null: nothing was spent, so it + # never affects spend or the budget, it just makes the drop visible in + # the activity log and countable as an error. + await log_usage( + db=db, + log_writer=log_writer, + api_key_id=api_key_id, + model=model, + provider=gate_instance, + endpoint=adapter.endpoint, + user_id=user_id, + error=no_pricing_detail, + latency_ms=_elapsed_ms(started_at), + counts_toward_budget=not budget_exempt, + ) raise adapter.error( 402, - no_pricing_error_detail(model), + no_pricing_detail, ErrorKind.INVALID_REQUEST, ) diff --git a/src/gateway/static/dashboard/assets/ActivityPage-D2jsRt9a.js b/src/gateway/static/dashboard/assets/ActivityPage-D2jsRt9a.js new file mode 100644 index 00000000..9d05b0a8 --- /dev/null +++ b/src/gateway/static/dashboard/assets/ActivityPage-D2jsRt9a.js @@ -0,0 +1 @@ +import{j as s}from"./tanstack-query-1t81HyiD.js";import{u as ze,r as n}from"./react-dgEcD0HR.js";import{u as We,a as Ye,b as Ge,c as he,d as xe,f as O,e as He,g as Ve,h as Ze,P as Je,E as Qe,C as ve,A as N,R as Xe,F as be,i as W,j as G,k as et}from"./index-Br3lxdY5.js";import{A as tt,F as st}from"./FilterChips-BbDynOdb.js";import{u as at,r as rt,B as lt}from"./tableSelection-9hV37uhu.js";import{C as nt}from"./ConfirmDialog-KQrh5qGO.js";import{D as ot}from"./DataTable-Bmn_eYSV.js";import{T as it,S as ct,P as dt}from"./TablePagination-BR55oZ8k.js";import{B as Y}from"./heroui-BI50yK5B.js";import"./recharts-C4kRDmvS.js";function ut(t){const[l,i]=ze(),a=n.useCallback(h=>l.get(h)??t[h],[l,t]),o=n.useCallback(h=>{const m=Number.parseInt(l.get(h)??"",10);if(!Number.isNaN(m))return m;const c=Number.parseInt(t[h],10);return Number.isNaN(c)?0:c},[l,t]),S=n.useCallback(h=>{i(m=>{const c=new URLSearchParams(m);for(const[p,x]of Object.entries(h)){const f=String(x);f===""||f===t[p]?c.delete(p):c.set(p,f)}return c},{replace:!0})},[i,t]);return{get:a,getNumber:o,patch:S}}const mt=new Intl.NumberFormat(void 0,{style:"currency",currency:"USD",maximumFractionDigits:4});function I(t){return t===null?"—":mt.format(t)}function _(t){return t===null?"—":t.toLocaleString()}function ke(t){return t===null?"—":t<1e3?`${t} ms`:`${(t/1e3).toFixed(t<1e4?2:1)} s`}function pt(t){const l=new Date(t);return Number.isNaN(l.getTime())?t:l.toLocaleString()}function gt(t){const l=new Date(t).getTime();if(Number.isNaN(l))return t;const i=Math.max(0,Math.round((Date.now()-l)/1e3));if(i<60)return`${i}s ago`;const a=Math.round(i/60);if(a<60)return`${a}m ago`;const o=Math.round(a/60);return o<24?`${o}h ago`:`${Math.round(o/24)}d ago`}const ht=t=>t.id,xt=t=>t.status==="error"?"bg-red-50":void 0,fe=[{label:"All",value:""},{label:"Success",value:"success"},{label:"Error",value:"error"}],_e=[{label:"All",value:""},{label:"Priced",value:"true"},{label:"Unpriced",value:"false"}],bt=50,ft={range:G,start_date:"",end_date:"",status:"",model:"",user_id:"",api_key_id:"",priced:"",page:"0",size:String(bt)};function y(t,l,i){if(l||i)return{start:l||void 0,end:i||void 0};if(t===ve)return{};const a=O(N,t)??O(N,G),o=(a==null?void 0:a.seconds)??null;return{start:o==null?void 0:et(o),end:void 0}}function _t({status:t}){const l=t==="error"?"border-red-200 bg-red-50 text-red-700":"border-[var(--otari-line)] bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]";return s.jsx("span",{className:`inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium ${l}`,children:t})}const vt={gateway:"Gateway",claude_code:"Claude Code",codex:"Codex"};function kt(t){return vt[t]??t}function u({label:t,children:l}){return s.jsxs("div",{className:"flex flex-col gap-0.5",children:[s.jsx("span",{className:"text-[11px] font-medium uppercase tracking-wide text-[var(--otari-muted)]",children:t}),s.jsx("span",{className:"text-sm text-[var(--otari-ink)] break-all",children:l})]})}function St({entry:t}){var l;return s.jsxs("div",{className:"flex flex-col gap-4 px-4 py-4",children:[t.error_message?s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx("span",{className:"text-[11px] font-medium uppercase tracking-wide text-[var(--otari-muted)]",children:"Error"}),s.jsx("pre",{className:"max-h-48 overflow-auto rounded-lg border border-red-200 bg-red-50 p-3 text-xs whitespace-pre-wrap break-all text-red-700",children:"This request failed. Inspect gateway logs for details."})]}):null,s.jsxs("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-4",children:[s.jsx(u,{label:"Provider",children:t.provider??"—"}),s.jsx(u,{label:"Endpoint",children:t.endpoint}),s.jsx(u,{label:"Source",children:kt(t.source)}),t.source_label?s.jsx(u,{label:"Session",children:t.source_label}):null,s.jsx(u,{label:"User",children:t.user_id??"—"}),s.jsx(u,{label:"API key",children:t.api_key_id??"—"}),s.jsx(u,{label:"Prompt tokens",children:_(t.prompt_tokens)}),s.jsx(u,{label:"Completion tokens",children:_(t.completion_tokens)}),s.jsx(u,{label:"Total tokens",children:_(t.total_tokens)}),s.jsx(u,{label:"Cost",children:I(t.cost)}),s.jsx(u,{label:"Cache read tokens",children:_(t.cache_read_tokens)}),s.jsx(u,{label:"Cache write tokens",children:_(t.cache_write_tokens)}),s.jsx(u,{label:"1h cache writes",children:_(t.cache_write_1h_tokens??null)}),s.jsx(u,{label:"Total time",children:ke(t.latency_ms)}),s.jsx(u,{label:"Request ID",children:t.id})]}),(l=t.pricing_breakdown)!=null&&l.length?s.jsxs("div",{className:"flex flex-col gap-2",children:[s.jsx("span",{className:"text-[11px] font-medium uppercase tracking-wide text-[var(--otari-muted)]",children:"Billed meters"}),s.jsx("div",{className:"grid gap-2 sm:grid-cols-2 lg:grid-cols-3",children:t.pricing_breakdown.map(i=>s.jsxs(u,{label:i.meter.replaceAll("_"," "),children:[_(i.units)," at ",I(i.rate_per_million)," / 1M, ",I(i.cost)]},i.meter))})]}):null]})}function It(){var ce,de,ue,me,pe;const t=We(),l=Ye(),i=n.useMemo(()=>{const e=new Map;for(const r of l.data??[])e.set(r.id,r.key_name??`${r.id.slice(0,8)}…`);return e},[l.data]),a=ut(ft),o=a.get("range"),S=a.get("start_date"),h=a.get("end_date"),m=a.get("status"),c=a.get("model"),p=a.get("user_id"),x=a.get("api_key_id"),f=a.get("priced"),H=Math.max(0,a.getNumber("page")),V=a.getNumber("size"),U=dt.reduce((e,r)=>Math.abs(r-V)y(o,S,h));n.useEffect(()=>{Z(y(o,S,h))},[o,S,h]);const[A,J]=n.useState(()=>y(o,"",""));n.useEffect(()=>{J(y(o,"",""))},[o]);const Se=!!(d.start||d.end),E=f==="true"?!0:f==="false"?!1:void 0,b=n.useMemo(()=>({start_date:d.start,end_date:d.end,status:m||void 0,model:c.trim()||void 0,user_id:p||void 0,api_key_id:x||void 0,priced:E}),[d,m,c,p,x,E]),g=at(),F=JSON.stringify(b),Q=n.useRef(F);n.useEffect(()=>{Q.current!==F&&(Q.current=F,a.patch({page:0}),g.clear())},[F,a,g]);const v=Ge(b,H,U),j=he(b),ye=n.useMemo(()=>({start_date:d.start,end_date:d.end,status:m||void 0,user_id:p||void 0,api_key_id:x||void 0}),[d,m,p,x]),X=xe(ye,"day"),L=((de=(ce=X.data)==null?void 0:ce.by_model)==null?void 0:de.filter(e=>!e.is_other&&e.key!==null).map(e=>e.key))??[],ee=(l.data??[]).map(e=>({value:e.id,label:e.key_name??`${e.id.slice(0,8)}…`})),w=O(N,o)??O(N,G),C=!!(d.start&&A.start&&new Date(d.start).getTime()({start_date:C?d.start:A.start,end_date:C?d.end:void 0,status:m||void 0,model:c.trim()||void 0,user_id:p||void 0,api_key_id:x||void 0,priced:E}),[C,d,A,m,c,p,x,E]),R=xe(we,te),Ce=(((ue=R.data)==null?void 0:ue.series)??[]).map(e=>({bucketStart:e.bucket_start,requests:e.requests})),k=v.data??[],Pe=j.isSuccess&&!j.isPlaceholderData?((me=j.data)==null?void 0:me.total)??0:null,Ne=!!(m||c.trim()||p||x||f||Se),M=(e,r)=>{var ge;return((ge=e.find(qe=>qe.value===r))==null?void 0:ge.label)??r},se=(t.data??[]).map(e=>({value:e.user_id,label:e.alias?`${e.alias} (${e.user_id})`:e.user_id})),Ae=()=>a.patch({status:"",priced:"",model:"",user_id:"",api_key_id:""}),Ee=[...m?[{key:"status",label:"Status",value:M(fe,m),onClear:()=>a.patch({status:""})}]:[],...f?[{key:"priced",label:"Priced",value:M(_e,f),onClear:()=>a.patch({priced:""})}]:[],...p?[{key:"user",label:"User",value:M(se,p),onClear:()=>a.patch({user_id:""})}]:[],...c.trim()?[{key:"model",label:"Model",value:c.trim(),onClear:()=>a.patch({model:""})}]:[],...x?[{key:"key",label:"API key",value:M(ee,x),onClear:()=>a.patch({api_key_id:""})}]:[]],K=n.useMemo(()=>k.filter(e=>!e.counts_toward_budget).map(e=>e.id),[k]),Fe=n.useMemo(()=>k.filter(e=>e.counts_toward_budget).map(e=>e.id),[k]),ae=rt(g.selectedKeys,K),P=ae.length,re=g.allMatching||P>0,Me=n.useMemo(()=>({...b,counts_toward_budget:!1}),[b]),le=he(Me,re),T=le.isSuccess?((pe=le.data)==null?void 0:pe.total)??null:null,Te=K.length>0&&P===K.length&&T!=null&&T>P,D=g.allMatching?T??P:P,B=Ve(),$=Ze(),[De,q]=n.useState(!1),[Ie,z]=n.useState(!1),[Oe,ne]=n.useState(null),Ue=n.useCallback(e=>s.jsxs("div",{children:[s.jsxs("div",{className:"flex items-center justify-between border-b border-[var(--otari-line)] px-4 py-2",children:[s.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Request detail"}),s.jsx(Y,{size:"sm",variant:"ghost",onPress:()=>ne(null),children:"Close"})]}),s.jsx(St,{entry:e})]}),[]),oe=()=>g.allMatching?{by_filter:!0,model:b.model,user_id:b.user_id,api_key_id:b.api_key_id,status:b.status,start_date:b.start_date,end_date:b.end_date,priced:b.priced}:{ids:ae},Le=()=>{B.mutate(oe(),{onSuccess:()=>{q(!1),g.clear()}})},Re=e=>{$.mutate({...oe(),...e},{onSuccess:()=>{z(!1),g.clear()}})},Ke=()=>{Z(y(o,S,h)),J(y(o,"","")),v.refetch(),j.refetch(),R.refetch(),X.refetch()},ie=e=>a.patch({range:e.key,start_date:"",end_date:""}),Be=(e,r)=>a.patch({start_date:e,end_date:r}),$e=n.useMemo(()=>{const e=r=>r===null?"—":i.get(r)??`${r.slice(0,8)}…`;return[{id:"time",header:"Time",cell:r=>s.jsx("span",{title:pt(r.timestamp),className:"text-[var(--otari-muted)]",children:gt(r.timestamp)})},{id:"user",header:"User",cell:r=>r.user_id??"—"},{id:"model",header:"Model",isRowHeader:!0,cell:r=>r.model},{id:"api_key",header:"API key",cell:r=>s.jsx("span",{className:"text-[var(--otari-muted)]",children:e(r.api_key_id)})},{id:"tokens",header:"Tokens",align:"end",cell:r=>_(r.total_tokens)},{id:"cost",header:"Cost",align:"end",cell:r=>I(r.cost)},{id:"latency",header:"Total time",align:"end",cell:r=>ke(r.latency_ms)},{id:"status",header:"Status",cell:r=>s.jsx(_t,{status:r.status})}]},[i]);return s.jsxs("div",{className:"flex flex-col gap-6",children:[s.jsx(Je,{title:"Activity",description:"A per-request log of what the gateway served: tokens, cost, latency, and failures. No request or response content is stored."}),s.jsx(Qe,{error:v.error??j.error}),s.jsxs("div",{className:"flex flex-col gap-3",children:[s.jsx(tt,{presets:N,extentKey:je,onPreset:ie,onSelectRange:Be,onSelectFull:()=>w?ie(w):void 0,series:Ce,bucket:te,windowStart:d.start,windowEnd:d.end,loading:R.isLoading,ariaLabel:"Activity request volume over the selected window",action:s.jsx(Xe,{onRefresh:Ke,isFetching:v.isFetching,updatedAt:v.dataUpdatedAt})}),s.jsxs(st,{chips:Ee,onClearAll:Ae,children:[s.jsx(be,{id:"filter-status",label:"Status",value:m,onChange:e=>a.patch({status:e}),children:fe.map(e=>s.jsx("option",{value:e.value,children:e.label},e.value))}),s.jsx(be,{id:"filter-priced",label:"Priced?",value:f,onChange:e=>a.patch({priced:e}),children:_e.map(e=>s.jsx("option",{value:e.value,children:e.label},e.value))}),s.jsx(W,{label:"API key",value:x,onChange:e=>a.patch({api_key_id:e}),placeholder:"All keys",options:ee}),s.jsx(W,{label:"User",value:p,onChange:e=>a.patch({user_id:e}),placeholder:"All users",options:se}),s.jsx(W,{label:"Model",value:c,onChange:e=>a.patch({model:e}),allowsCustom:!0,placeholder:"Any model",options:(c&&!L.includes(c)?[c,...L]:L).map(e=>({value:e,label:e}))})]})]}),re?s.jsxs(lt,{selectedCount:D,allMatching:g.allMatching,matchingTotal:T,canSelectAllMatching:Te,onSelectAllMatching:g.enableAllMatching,onClear:g.clear,children:[s.jsx(Y,{size:"sm",variant:"primary",onPress:()=>z(!0),children:"Set price"}),s.jsx(Y,{size:"sm",variant:"danger",onPress:()=>q(!0),children:"Delete"})]}):null,s.jsx(ot,{ariaLabel:"Activity log",columns:$e,rows:k,getRowKey:ht,isLoading:v.isLoading,emptyContent:Ne?"No requests match these filters.":"No requests recorded yet.",selectionMode:"multiple",selectedKeys:g.selectedKeys,onSelectionChange:g.onSelectionChange,disabledKeys:Fe,onRowAction:e=>ne(r=>r===e?null:e),rowClassName:xt,detailKey:Oe,renderDetail:Ue}),s.jsx(it,{page:H,pageSize:U,total:Pe,rowsOnPage:k.length,onPageChange:e=>a.patch({page:e}),onPageSizeChange:e=>a.patch({size:e,page:0}),isFetching:v.isFetching,hasNextFallback:k.length===U}),s.jsx(nt,{isOpen:De,onOpenChange:q,heading:"Delete usage rows",body:`Delete ${D.toLocaleString()} imported ${D===1?"row":"rows"}? Only imported rows are removed, and this cannot be undone.`,confirmLabel:"Delete",isPending:B.isPending,error:B.error,onConfirm:Le}),s.jsx(ct,{isOpen:Ie,onOpenChange:z,targetCount:D,isPending:$.isPending,error:$.error,onSubmit:Re})]})}export{It as ActivityPage}; diff --git a/src/gateway/static/dashboard/assets/ActivityPage-DIACtZv5.js b/src/gateway/static/dashboard/assets/ActivityPage-DIACtZv5.js deleted file mode 100644 index 33a2e1d9..00000000 --- a/src/gateway/static/dashboard/assets/ActivityPage-DIACtZv5.js +++ /dev/null @@ -1 +0,0 @@ -import{j as s}from"./tanstack-query-1t81HyiD.js";import{u as ze,r as n}from"./react-dgEcD0HR.js";import{u as We,a as Ye,b as Ge,c as he,d as xe,e as He,f as Ve,P as Ze,E as Je,R as Qe,F as be,g as W}from"./index-DV8qvdUj.js";import{f as O,b as Xe,A as et,C as ve,a as N,F as tt,c as G,i as st}from"./FilterChips-DZjxf-ot.js";import{u as at,r as rt,B as lt}from"./tableSelection-9hV37uhu.js";import{C as nt}from"./ConfirmDialog-KvA7b1-a.js";import{D as ot}from"./DataTable-Bmn_eYSV.js";import{T as it,S as ct,P as dt}from"./TablePagination-CXhesdmH.js";import{B as Y}from"./heroui-BI50yK5B.js";import"./recharts-C4kRDmvS.js";function ut(t){const[l,i]=ze(),a=n.useCallback(h=>l.get(h)??t[h],[l,t]),o=n.useCallback(h=>{const m=Number.parseInt(l.get(h)??"",10);if(!Number.isNaN(m))return m;const c=Number.parseInt(t[h],10);return Number.isNaN(c)?0:c},[l,t]),S=n.useCallback(h=>{i(m=>{const c=new URLSearchParams(m);for(const[p,x]of Object.entries(h)){const f=String(x);f===""||f===t[p]?c.delete(p):c.set(p,f)}return c},{replace:!0})},[i,t]);return{get:a,getNumber:o,patch:S}}const mt=new Intl.NumberFormat(void 0,{style:"currency",currency:"USD",maximumFractionDigits:4});function I(t){return t===null?"—":mt.format(t)}function _(t){return t===null?"—":t.toLocaleString()}function ke(t){return t===null?"—":t<1e3?`${t} ms`:`${(t/1e3).toFixed(t<1e4?2:1)} s`}function pt(t){const l=new Date(t);return Number.isNaN(l.getTime())?t:l.toLocaleString()}function gt(t){const l=new Date(t).getTime();if(Number.isNaN(l))return t;const i=Math.max(0,Math.round((Date.now()-l)/1e3));if(i<60)return`${i}s ago`;const a=Math.round(i/60);if(a<60)return`${a}m ago`;const o=Math.round(a/60);return o<24?`${o}h ago`:`${Math.round(o/24)}d ago`}const ht=t=>t.id,xt=t=>t.status==="error"?"bg-red-50":void 0,fe=[{label:"All",value:""},{label:"Success",value:"success"},{label:"Error",value:"error"}],_e=[{label:"All",value:""},{label:"Priced",value:"true"},{label:"Unpriced",value:"false"}],bt=50,ft={range:G,start_date:"",end_date:"",status:"",model:"",user_id:"",api_key_id:"",priced:"",page:"0",size:String(bt)};function y(t,l,i){if(l||i)return{start:l||void 0,end:i||void 0};if(t===ve)return{};const a=O(N,t)??O(N,G),o=(a==null?void 0:a.seconds)??null;return{start:o==null?void 0:st(o),end:void 0}}function _t({status:t}){const l=t==="error"?"border-red-200 bg-red-50 text-red-700":"border-[var(--otari-line)] bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]";return s.jsx("span",{className:`inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium ${l}`,children:t})}const vt={gateway:"Gateway",claude_code:"Claude Code",codex:"Codex"};function kt(t){return vt[t]??t}function u({label:t,children:l}){return s.jsxs("div",{className:"flex flex-col gap-0.5",children:[s.jsx("span",{className:"text-[11px] font-medium uppercase tracking-wide text-[var(--otari-muted)]",children:t}),s.jsx("span",{className:"text-sm text-[var(--otari-ink)] break-all",children:l})]})}function St({entry:t}){var l;return s.jsxs("div",{className:"flex flex-col gap-4 px-4 py-4",children:[t.error_message?s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx("span",{className:"text-[11px] font-medium uppercase tracking-wide text-[var(--otari-muted)]",children:"Error"}),s.jsx("pre",{className:"max-h-48 overflow-auto rounded-lg border border-red-200 bg-red-50 p-3 text-xs whitespace-pre-wrap break-all text-red-700",children:"The provider returned an error. Inspect gateway logs for details."})]}):null,s.jsxs("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-4",children:[s.jsx(u,{label:"Provider",children:t.provider??"—"}),s.jsx(u,{label:"Endpoint",children:t.endpoint}),s.jsx(u,{label:"Source",children:kt(t.source)}),t.source_label?s.jsx(u,{label:"Session",children:t.source_label}):null,s.jsx(u,{label:"User",children:t.user_id??"—"}),s.jsx(u,{label:"API key",children:t.api_key_id??"—"}),s.jsx(u,{label:"Prompt tokens",children:_(t.prompt_tokens)}),s.jsx(u,{label:"Completion tokens",children:_(t.completion_tokens)}),s.jsx(u,{label:"Total tokens",children:_(t.total_tokens)}),s.jsx(u,{label:"Cost",children:I(t.cost)}),s.jsx(u,{label:"Cache read tokens",children:_(t.cache_read_tokens)}),s.jsx(u,{label:"Cache write tokens",children:_(t.cache_write_tokens)}),s.jsx(u,{label:"1h cache writes",children:_(t.cache_write_1h_tokens??null)}),s.jsx(u,{label:"Total time",children:ke(t.latency_ms)}),s.jsx(u,{label:"Request ID",children:t.id})]}),(l=t.pricing_breakdown)!=null&&l.length?s.jsxs("div",{className:"flex flex-col gap-2",children:[s.jsx("span",{className:"text-[11px] font-medium uppercase tracking-wide text-[var(--otari-muted)]",children:"Billed meters"}),s.jsx("div",{className:"grid gap-2 sm:grid-cols-2 lg:grid-cols-3",children:t.pricing_breakdown.map(i=>s.jsxs(u,{label:i.meter.replaceAll("_"," "),children:[_(i.units)," at ",I(i.rate_per_million)," / 1M, ",I(i.cost)]},i.meter))})]}):null]})}function It(){var ce,de,ue,me,pe;const t=We(),l=Ye(),i=n.useMemo(()=>{const e=new Map;for(const r of l.data??[])e.set(r.id,r.key_name??`${r.id.slice(0,8)}…`);return e},[l.data]),a=ut(ft),o=a.get("range"),S=a.get("start_date"),h=a.get("end_date"),m=a.get("status"),c=a.get("model"),p=a.get("user_id"),x=a.get("api_key_id"),f=a.get("priced"),H=Math.max(0,a.getNumber("page")),V=a.getNumber("size"),U=dt.reduce((e,r)=>Math.abs(r-V)y(o,S,h));n.useEffect(()=>{Z(y(o,S,h))},[o,S,h]);const[A,J]=n.useState(()=>y(o,"",""));n.useEffect(()=>{J(y(o,"",""))},[o]);const Se=!!(d.start||d.end),E=f==="true"?!0:f==="false"?!1:void 0,b=n.useMemo(()=>({start_date:d.start,end_date:d.end,status:m||void 0,model:c.trim()||void 0,user_id:p||void 0,api_key_id:x||void 0,priced:E}),[d,m,c,p,x,E]),g=at(),F=JSON.stringify(b),Q=n.useRef(F);n.useEffect(()=>{Q.current!==F&&(Q.current=F,a.patch({page:0}),g.clear())},[F,a,g]);const v=Ge(b,H,U),j=he(b),ye=n.useMemo(()=>({start_date:d.start,end_date:d.end,status:m||void 0,user_id:p||void 0,api_key_id:x||void 0}),[d,m,p,x]),X=xe(ye,"day"),L=((de=(ce=X.data)==null?void 0:ce.by_model)==null?void 0:de.filter(e=>!e.is_other&&e.key!==null).map(e=>e.key))??[],ee=(l.data??[]).map(e=>({value:e.id,label:e.key_name??`${e.id.slice(0,8)}…`})),w=O(N,o)??O(N,G),C=!!(d.start&&A.start&&new Date(d.start).getTime()({start_date:C?d.start:A.start,end_date:C?d.end:void 0,status:m||void 0,model:c.trim()||void 0,user_id:p||void 0,api_key_id:x||void 0,priced:E}),[C,d,A,m,c,p,x,E]),R=xe(we,te),Ce=(((ue=R.data)==null?void 0:ue.series)??[]).map(e=>({bucketStart:e.bucket_start,requests:e.requests})),k=v.data??[],Pe=j.isSuccess&&!j.isPlaceholderData?((me=j.data)==null?void 0:me.total)??0:null,Ne=!!(m||c.trim()||p||x||f||Se),M=(e,r)=>{var ge;return((ge=e.find(qe=>qe.value===r))==null?void 0:ge.label)??r},se=(t.data??[]).map(e=>({value:e.user_id,label:e.alias?`${e.alias} (${e.user_id})`:e.user_id})),Ae=()=>a.patch({status:"",priced:"",model:"",user_id:"",api_key_id:""}),Ee=[...m?[{key:"status",label:"Status",value:M(fe,m),onClear:()=>a.patch({status:""})}]:[],...f?[{key:"priced",label:"Priced",value:M(_e,f),onClear:()=>a.patch({priced:""})}]:[],...p?[{key:"user",label:"User",value:M(se,p),onClear:()=>a.patch({user_id:""})}]:[],...c.trim()?[{key:"model",label:"Model",value:c.trim(),onClear:()=>a.patch({model:""})}]:[],...x?[{key:"key",label:"API key",value:M(ee,x),onClear:()=>a.patch({api_key_id:""})}]:[]],K=n.useMemo(()=>k.filter(e=>!e.counts_toward_budget).map(e=>e.id),[k]),Fe=n.useMemo(()=>k.filter(e=>e.counts_toward_budget).map(e=>e.id),[k]),ae=rt(g.selectedKeys,K),P=ae.length,re=g.allMatching||P>0,Me=n.useMemo(()=>({...b,counts_toward_budget:!1}),[b]),le=he(Me,re),T=le.isSuccess?((pe=le.data)==null?void 0:pe.total)??null:null,Te=K.length>0&&P===K.length&&T!=null&&T>P,D=g.allMatching?T??P:P,B=He(),$=Ve(),[De,q]=n.useState(!1),[Ie,z]=n.useState(!1),[Oe,ne]=n.useState(null),Ue=n.useCallback(e=>s.jsxs("div",{children:[s.jsxs("div",{className:"flex items-center justify-between border-b border-[var(--otari-line)] px-4 py-2",children:[s.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Request detail"}),s.jsx(Y,{size:"sm",variant:"ghost",onPress:()=>ne(null),children:"Close"})]}),s.jsx(St,{entry:e})]}),[]),oe=()=>g.allMatching?{by_filter:!0,model:b.model,user_id:b.user_id,api_key_id:b.api_key_id,status:b.status,start_date:b.start_date,end_date:b.end_date,priced:b.priced}:{ids:ae},Le=()=>{B.mutate(oe(),{onSuccess:()=>{q(!1),g.clear()}})},Re=e=>{$.mutate({...oe(),...e},{onSuccess:()=>{z(!1),g.clear()}})},Ke=()=>{Z(y(o,S,h)),J(y(o,"","")),v.refetch(),j.refetch(),R.refetch(),X.refetch()},ie=e=>a.patch({range:e.key,start_date:"",end_date:""}),Be=(e,r)=>a.patch({start_date:e,end_date:r}),$e=n.useMemo(()=>{const e=r=>r===null?"—":i.get(r)??`${r.slice(0,8)}…`;return[{id:"time",header:"Time",cell:r=>s.jsx("span",{title:pt(r.timestamp),className:"text-[var(--otari-muted)]",children:gt(r.timestamp)})},{id:"user",header:"User",cell:r=>r.user_id??"—"},{id:"model",header:"Model",isRowHeader:!0,cell:r=>r.model},{id:"api_key",header:"API key",cell:r=>s.jsx("span",{className:"text-[var(--otari-muted)]",children:e(r.api_key_id)})},{id:"tokens",header:"Tokens",align:"end",cell:r=>_(r.total_tokens)},{id:"cost",header:"Cost",align:"end",cell:r=>I(r.cost)},{id:"latency",header:"Total time",align:"end",cell:r=>ke(r.latency_ms)},{id:"status",header:"Status",cell:r=>s.jsx(_t,{status:r.status})}]},[i]);return s.jsxs("div",{className:"flex flex-col gap-6",children:[s.jsx(Ze,{title:"Activity",description:"A per-request log of what the gateway served: tokens, cost, latency, and failures. No request or response content is stored."}),s.jsx(Je,{error:v.error??j.error}),s.jsxs("div",{className:"flex flex-col gap-3",children:[s.jsx(et,{presets:N,extentKey:je,onPreset:ie,onSelectRange:Be,onSelectFull:()=>w?ie(w):void 0,series:Ce,bucket:te,windowStart:d.start,windowEnd:d.end,loading:R.isLoading,ariaLabel:"Activity request volume over the selected window",action:s.jsx(Qe,{onRefresh:Ke,isFetching:v.isFetching,updatedAt:v.dataUpdatedAt})}),s.jsxs(tt,{chips:Ee,onClearAll:Ae,children:[s.jsx(be,{id:"filter-status",label:"Status",value:m,onChange:e=>a.patch({status:e}),children:fe.map(e=>s.jsx("option",{value:e.value,children:e.label},e.value))}),s.jsx(be,{id:"filter-priced",label:"Priced?",value:f,onChange:e=>a.patch({priced:e}),children:_e.map(e=>s.jsx("option",{value:e.value,children:e.label},e.value))}),s.jsx(W,{label:"API key",value:x,onChange:e=>a.patch({api_key_id:e}),placeholder:"All keys",options:ee}),s.jsx(W,{label:"User",value:p,onChange:e=>a.patch({user_id:e}),placeholder:"All users",options:se}),s.jsx(W,{label:"Model",value:c,onChange:e=>a.patch({model:e}),allowsCustom:!0,placeholder:"Any model",options:(c&&!L.includes(c)?[c,...L]:L).map(e=>({value:e,label:e}))})]})]}),re?s.jsxs(lt,{selectedCount:D,allMatching:g.allMatching,matchingTotal:T,canSelectAllMatching:Te,onSelectAllMatching:g.enableAllMatching,onClear:g.clear,children:[s.jsx(Y,{size:"sm",variant:"primary",onPress:()=>z(!0),children:"Set price"}),s.jsx(Y,{size:"sm",variant:"danger",onPress:()=>q(!0),children:"Delete"})]}):null,s.jsx(ot,{ariaLabel:"Activity log",columns:$e,rows:k,getRowKey:ht,isLoading:v.isLoading,emptyContent:Ne?"No requests match these filters.":"No requests recorded yet.",selectionMode:"multiple",selectedKeys:g.selectedKeys,onSelectionChange:g.onSelectionChange,disabledKeys:Fe,onRowAction:e=>ne(r=>r===e?null:e),rowClassName:xt,detailKey:Oe,renderDetail:Ue}),s.jsx(it,{page:H,pageSize:U,total:Pe,rowsOnPage:k.length,onPageChange:e=>a.patch({page:e}),onPageSizeChange:e=>a.patch({size:e,page:0}),isFetching:v.isFetching,hasNextFallback:k.length===U}),s.jsx(nt,{isOpen:De,onOpenChange:q,heading:"Delete usage rows",body:`Delete ${D.toLocaleString()} imported ${D===1?"row":"rows"}? Only imported rows are removed, and this cannot be undone.`,confirmLabel:"Delete",isPending:B.isPending,error:B.error,onConfirm:Le}),s.jsx(ct,{isOpen:Ie,onOpenChange:z,targetCount:D,isPending:$.isPending,error:$.error,onSubmit:Re})]})}export{It as ActivityPage}; diff --git a/src/gateway/static/dashboard/assets/AliasesPage-x0NFKgtH.js b/src/gateway/static/dashboard/assets/AliasesPage-CrabMwlU.js similarity index 82% rename from src/gateway/static/dashboard/assets/AliasesPage-x0NFKgtH.js rename to src/gateway/static/dashboard/assets/AliasesPage-CrabMwlU.js index 6789fe8c..849928a2 100644 --- a/src/gateway/static/dashboard/assets/AliasesPage-x0NFKgtH.js +++ b/src/gateway/static/dashboard/assets/AliasesPage-CrabMwlU.js @@ -1 +1 @@ -import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as d,u as L}from"./react-dgEcD0HR.js";import{h as T,i as E,j as M,C as I,P as K,E as S,k as A}from"./index-DV8qvdUj.js";import{u as R,r as F,B as $}from"./tableSelection-9hV37uhu.js";import{C as q}from"./ConfirmDialog-KvA7b1-a.js";import{D as O}from"./DataTable-Bmn_eYSV.js";import{F as V}from"./Field-Dwp6u3t8.js";import{C as N,L as z,I as H,a as G,b as W,f as X,B as h,d as k}from"./heroui-BI50yK5B.js";const _=50;function P({label:t,value:a,onChange:r,description:n,placeholder:u="provider:model",autoFocus:i,isRequired:m}){const c=T(),{visible:o,total:x,failed:p}=d.useMemo(()=>{var y;const l=a.trim().toLowerCase(),f=((y=c.data)==null?void 0:y.providers)??[],C=f.flatMap(g=>g.models),j=l?C.filter(g=>g.key.toLowerCase().includes(l)):C;return{visible:j.slice(0,_),total:j.length,failed:f.filter(g=>!g.ok)}},[c.data,a]),b=c.isLoading?"Loading models from your providers…":p.length>0?`Could not list models for ${p.map(f=>f.provider).join(", ")}. Check that provider's credentials, or type the model key directly.`:x>o.length?`Showing ${o.length} of ${x} matches. Keep typing to narrow them.`:n;return e.jsxs(N.Root,{allowsCustomValue:!0,allowsEmptyCollection:!0,menuTrigger:"input",inputValue:a,onInputChange:r,onSelectionChange:l=>{l!=null&&r(String(l))},isRequired:m,className:"flex max-w-md flex-col gap-1",children:[e.jsx(z,{className:"text-sm font-medium text-[var(--otari-ink)]",children:t}),e.jsxs(N.InputGroup,{children:[e.jsx(H,{placeholder:u,autoFocus:i}),e.jsx(N.Trigger,{})]}),e.jsx(N.Popover,{children:e.jsx(G,{items:o,className:"max-h-72 overflow-auto",children:l=>e.jsx(W,{id:l.key,textValue:l.key,children:l.key})})}),b?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:b}):null]})}const J=t=>t.name;function Q({alias:t,onClose:a}){const r=A(),[n,u]=d.useState(t.target),i=n.trim()!==""&&n.trim()!==t.target,m=()=>{i&&r.mutate({name:t.name,target:n.trim()},{onSuccess:a})};return e.jsx(k,{children:e.jsxs(k.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsxs("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:["Edit alias ",e.jsx("code",{children:t.name})]}),e.jsx(S,{error:r.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Alias name"}),e.jsx("code",{className:"text-sm text-[var(--otari-muted)]",children:t.name}),e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"The alias name is the key and cannot be changed here. Delete and recreate to rename."})]}),e.jsx(P,{label:"Target",value:n,onChange:u,isRequired:!0,description:"The real model this resolves to. Callers never see it."})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(h,{variant:"primary",isDisabled:!i||r.isPending,onPress:m,children:r.isPending?"Saving…":"Save changes"}),e.jsx(h,{variant:"ghost",onPress:a,children:"Cancel"})]})]})})}function U({onClose:t,initialTarget:a=""}){const r=A(),[n,u]=d.useState(""),[i,m]=d.useState(a),c=/[:/]/.test(n),o=n.trim()!==""&&i.trim()!==""&&!c,x=()=>{o&&r.mutate({name:n.trim(),target:i.trim()},{onSuccess:t})};return e.jsx(k,{children:e.jsxs(k.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsx("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"New alias"}),e.jsx(S,{error:r.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(V,{label:"Alias name",value:n,onChange:u,placeholder:"fast-model",isRequired:!0,autoFocus:!0,description:c?e.jsx("span",{className:"text-red-700",children:"An alias name cannot contain “:” or “/”."}):"What callers send as `model`."}),e.jsx(P,{label:"Target",value:i,onChange:m,isRequired:!0,description:"The real model this resolves to. Callers never see it."})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(h,{variant:"primary",isDisabled:!o||r.isPending,onPress:x,children:r.isPending?"Creating…":"Create alias"}),e.jsx(h,{variant:"ghost",onPress:t,children:"Cancel"})]})]})})}function le(){const t=E(),a=M(),[r]=L(),n=r.get("target")??"",[u,i]=d.useState(n!==""),[m,c]=d.useState(null),o=R(),[x,p]=d.useState(!1),[b,l]=d.useState(void 0),[f,C]=d.useState(!1),j=[...t.data??[]].sort((s,B)=>s.name.localeCompare(B.name)),y=j.filter(s=>s.source==="stored").map(s=>s.name),g=j.filter(s=>s.source!=="stored").map(s=>s.name),v=F(o.selectedKeys,y),w=async()=>{C(!0),l(void 0);try{for(const s of v)await a.mutateAsync(s);o.clear(),p(!1)}catch(s){l(s)}finally{C(!1)}},D=d.useMemo(()=>[{id:"alias",header:"Alias",isRowHeader:!0,cell:s=>e.jsx("span",{className:"font-medium break-all text-[var(--otari-ink)]",children:s.name})},{id:"target",header:"Target",cell:s=>e.jsx("span",{className:"break-all text-[var(--otari-muted)]",children:s.target})},{id:"source",header:"Source",cell:s=>e.jsx(X,{size:"sm",color:s.source==="stored"?"accent":"default",children:s.source})},{id:"actions",header:"Actions",align:"end",cell:s=>s.source==="stored"?e.jsxs("span",{className:"inline-flex items-center gap-2 whitespace-nowrap",children:[e.jsx(h,{size:"sm",variant:"ghost",onPress:()=>{i(!1),c(s)},children:"Edit"}),e.jsx(I,{confirmLabel:"Delete",isPending:a.isPending,onConfirm:()=>a.mutate(s.name),children:"Delete"})]}):e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"set in config.yml"})}],[a.isPending,a.mutate]);return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(K,{title:"Aliases",description:"Friendly names that map to a real provider:model. Callers send the alias as the model; pricing, budgets, and usage key on the target.",action:u||m?null:e.jsx(h,{variant:"primary",onPress:()=>{c(null),i(!0)},children:"New alias"})}),e.jsx(S,{error:t.error}),u?e.jsx(U,{initialTarget:n,onClose:()=>i(!1)}):null,m?e.jsx(Q,{alias:m,onClose:()=>c(null)}):null,a.error?e.jsx(S,{error:a.error}):null,v.length>0?e.jsx($,{selectedCount:v.length,allMatching:!1,matchingTotal:null,canSelectAllMatching:!1,onSelectAllMatching:()=>{},onClear:o.clear,children:e.jsx(h,{size:"sm",variant:"danger",onPress:()=>p(!0),children:"Delete"})}):null,e.jsx(O,{ariaLabel:"Aliases",columns:D,rows:j,getRowKey:J,isLoading:t.isLoading,emptyContent:"No aliases yet. Create one to give a model a friendly name.",selectionMode:"multiple",selectedKeys:o.selectedKeys,onSelectionChange:o.onSelectionChange,disabledKeys:g}),e.jsx(q,{isOpen:x,onOpenChange:p,heading:"Delete aliases",body:`Delete ${v.length} stored ${v.length===1?"alias":"aliases"}? Callers using ${v.length===1?"it":"them"} will get a model-not-found error.`,confirmLabel:"Delete",isPending:f,error:b,onConfirm:w})]})}export{le as AliasesPage}; +import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as d,u as L}from"./react-dgEcD0HR.js";import{l as T,m as E,n as M,o as I,P as K,E as S,p as A}from"./index-Br3lxdY5.js";import{u as R,r as F,B as $}from"./tableSelection-9hV37uhu.js";import{C as q}from"./ConfirmDialog-KQrh5qGO.js";import{D as O}from"./DataTable-Bmn_eYSV.js";import{F as V}from"./Field-Dwp6u3t8.js";import{C as N,L as z,I as H,a as G,b as W,f as X,B as x,d as k}from"./heroui-BI50yK5B.js";const _=50;function P({label:t,value:a,onChange:r,description:n,placeholder:u="provider:model",autoFocus:i,isRequired:m}){const c=T(),{visible:o,total:h,failed:p}=d.useMemo(()=>{var y;const l=a.trim().toLowerCase(),f=((y=c.data)==null?void 0:y.providers)??[],C=f.flatMap(g=>g.models),j=l?C.filter(g=>g.key.toLowerCase().includes(l)):C;return{visible:j.slice(0,_),total:j.length,failed:f.filter(g=>!g.ok)}},[c.data,a]),b=c.isLoading?"Loading models from your providers…":p.length>0?`Could not list models for ${p.map(f=>f.provider).join(", ")}. Check that provider's credentials, or type the model key directly.`:h>o.length?`Showing ${o.length} of ${h} matches. Keep typing to narrow them.`:n;return e.jsxs(N.Root,{allowsCustomValue:!0,allowsEmptyCollection:!0,menuTrigger:"input",inputValue:a,onInputChange:r,onSelectionChange:l=>{l!=null&&r(String(l))},isRequired:m,className:"flex max-w-md flex-col gap-1",children:[e.jsx(z,{className:"text-sm font-medium text-[var(--otari-ink)]",children:t}),e.jsxs(N.InputGroup,{children:[e.jsx(H,{placeholder:u,autoFocus:i}),e.jsx(N.Trigger,{})]}),e.jsx(N.Popover,{children:e.jsx(G,{items:o,className:"max-h-72 overflow-auto",children:l=>e.jsx(W,{id:l.key,textValue:l.key,children:l.key})})}),b?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:b}):null]})}const J=t=>t.name;function Q({alias:t,onClose:a}){const r=A(),[n,u]=d.useState(t.target),i=n.trim()!==""&&n.trim()!==t.target,m=()=>{i&&r.mutate({name:t.name,target:n.trim()},{onSuccess:a})};return e.jsx(k,{children:e.jsxs(k.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsxs("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:["Edit alias ",e.jsx("code",{children:t.name})]}),e.jsx(S,{error:r.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Alias name"}),e.jsx("code",{className:"text-sm text-[var(--otari-muted)]",children:t.name}),e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"The alias name is the key and cannot be changed here. Delete and recreate to rename."})]}),e.jsx(P,{label:"Target",value:n,onChange:u,isRequired:!0,description:"The real model this resolves to. Callers never see it."})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(x,{variant:"primary",isDisabled:!i||r.isPending,onPress:m,children:r.isPending?"Saving…":"Save changes"}),e.jsx(x,{variant:"ghost",onPress:a,children:"Cancel"})]})]})})}function U({onClose:t,initialTarget:a=""}){const r=A(),[n,u]=d.useState(""),[i,m]=d.useState(a),c=/[:/]/.test(n),o=n.trim()!==""&&i.trim()!==""&&!c,h=()=>{o&&r.mutate({name:n.trim(),target:i.trim()},{onSuccess:t})};return e.jsx(k,{children:e.jsxs(k.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsx("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"New alias"}),e.jsx(S,{error:r.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(V,{label:"Alias name",value:n,onChange:u,placeholder:"fast-model",isRequired:!0,autoFocus:!0,description:c?e.jsx("span",{className:"text-red-700",children:"An alias name cannot contain “:” or “/”."}):"What callers send as `model`."}),e.jsx(P,{label:"Target",value:i,onChange:m,isRequired:!0,description:"The real model this resolves to. Callers never see it."})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(x,{variant:"primary",isDisabled:!o||r.isPending,onPress:h,children:r.isPending?"Creating…":"Create alias"}),e.jsx(x,{variant:"ghost",onPress:t,children:"Cancel"})]})]})})}function le(){const t=E(),a=M(),[r]=L(),n=r.get("target")??"",[u,i]=d.useState(n!==""),[m,c]=d.useState(null),o=R(),[h,p]=d.useState(!1),[b,l]=d.useState(void 0),[f,C]=d.useState(!1),j=[...t.data??[]].sort((s,B)=>s.name.localeCompare(B.name)),y=j.filter(s=>s.source==="stored").map(s=>s.name),g=j.filter(s=>s.source!=="stored").map(s=>s.name),v=F(o.selectedKeys,y),w=async()=>{C(!0),l(void 0);try{for(const s of v)await a.mutateAsync(s);o.clear(),p(!1)}catch(s){l(s)}finally{C(!1)}},D=d.useMemo(()=>[{id:"alias",header:"Alias",isRowHeader:!0,cell:s=>e.jsx("span",{className:"font-medium break-all text-[var(--otari-ink)]",children:s.name})},{id:"target",header:"Target",cell:s=>e.jsx("span",{className:"break-all text-[var(--otari-muted)]",children:s.target})},{id:"source",header:"Source",cell:s=>e.jsx(X,{size:"sm",color:s.source==="stored"?"accent":"default",children:s.source})},{id:"actions",header:"Actions",align:"end",cell:s=>s.source==="stored"?e.jsxs("span",{className:"inline-flex items-center gap-2 whitespace-nowrap",children:[e.jsx(x,{size:"sm",variant:"ghost",onPress:()=>{i(!1),c(s)},children:"Edit"}),e.jsx(I,{confirmLabel:"Delete",isPending:a.isPending,onConfirm:()=>a.mutate(s.name),children:"Delete"})]}):e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"set in config.yml"})}],[a.isPending,a.mutate]);return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(K,{title:"Aliases",description:"Friendly names that map to a real provider:model. Callers send the alias as the model; pricing, budgets, and usage key on the target.",action:u||m?null:e.jsx(x,{variant:"primary",onPress:()=>{c(null),i(!0)},children:"New alias"})}),e.jsx(S,{error:t.error}),u?e.jsx(U,{initialTarget:n,onClose:()=>i(!1)}):null,m?e.jsx(Q,{alias:m,onClose:()=>c(null)}):null,a.error?e.jsx(S,{error:a.error}):null,v.length>0?e.jsx($,{selectedCount:v.length,allMatching:!1,matchingTotal:null,canSelectAllMatching:!1,onSelectAllMatching:()=>{},onClear:o.clear,children:e.jsx(x,{size:"sm",variant:"danger",onPress:()=>p(!0),children:"Delete"})}):null,e.jsx(O,{ariaLabel:"Aliases",columns:D,rows:j,getRowKey:J,isLoading:t.isLoading,emptyContent:"No aliases yet. Create one to give a model a friendly name.",selectionMode:"multiple",selectedKeys:o.selectedKeys,onSelectionChange:o.onSelectionChange,disabledKeys:g}),e.jsx(q,{isOpen:h,onOpenChange:p,heading:"Delete aliases",body:`Delete ${v.length} stored ${v.length===1?"alias":"aliases"}? Callers using ${v.length===1?"it":"them"} will get a model-not-found error.`,confirmLabel:"Delete",isPending:f,error:b,onConfirm:w})]})}export{le as AliasesPage}; diff --git a/src/gateway/static/dashboard/assets/BudgetsPage-D-S9-U_l.js b/src/gateway/static/dashboard/assets/BudgetsPage-DjVks5Lb.js similarity index 98% rename from src/gateway/static/dashboard/assets/BudgetsPage-D-S9-U_l.js rename to src/gateway/static/dashboard/assets/BudgetsPage-DjVks5Lb.js index 98f17f26..0c4630b4 100644 --- a/src/gateway/static/dashboard/assets/BudgetsPage-D-S9-U_l.js +++ b/src/gateway/static/dashboard/assets/BudgetsPage-DjVks5Lb.js @@ -1 +1 @@ -import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as i}from"./react-dgEcD0HR.js";import{l as ae,u as re,m as le,n as ie,o as oe,p as de,P as ue,E as T,I as ce,q as me,r as xe}from"./index-DV8qvdUj.js";import{u as ge,r as he,B as pe}from"./tableSelection-9hV37uhu.js";import{C as fe}from"./ConfirmDialog-KvA7b1-a.js";import{D as be}from"./DataTable-Bmn_eYSV.js";import{F as z}from"./Field-Dwp6u3t8.js";import{C as E,I as je,a as ve,b as ye,B as x,d as k,S as Ne}from"./heroui-BI50yK5B.js";const Se=50;function _e({value:t,onChange:r,users:a,label:o,description:l}){const[g,h]=i.useState(""),d=i.useMemo(()=>a.filter(s=>!s.user_id.startsWith("apikey-")).map(s=>({id:s.user_id,label:s.alias?`${s.user_id} (${s.alias})`:s.user_id})),[a]),p=i.useMemo(()=>{const s=g.trim().toLowerCase();return d.filter(u=>!t.includes(u.id)).filter(u=>!s||u.id.toLowerCase().includes(s)||u.label.toLowerCase().includes(s)).slice(0,Se)},[d,t,g]),c=s=>{t.includes(s)||r([...t,s]),h("")},m=s=>r(t.filter(u=>u!==s));return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{children:[e.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:o}),l?e.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:l}):null]}),t.length>0?e.jsx("div",{className:"flex flex-wrap gap-1.5",children:t.map(s=>e.jsxs("span",{className:"inline-flex items-center gap-1 rounded-full bg-[var(--otari-brand-tint)] px-2.5 py-1 font-mono text-xs text-[var(--otari-brand-dark)]",children:[s,e.jsx("button",{type:"button","aria-label":`Remove ${s}`,onClick:()=>m(s),className:"text-[var(--otari-brand-dark)] hover:text-red-700",children:"×"})]},s))}):null,d.length===0?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"No users yet. Create users first, then assign them here or from the Users page."}):e.jsxs(E.Root,{allowsEmptyCollection:!0,menuTrigger:"input",inputValue:g,onInputChange:h,selectedKey:null,onSelectionChange:s=>{s!=null&&c(String(s))},className:"flex flex-col gap-1",children:[e.jsxs(E.InputGroup,{children:[e.jsx(je,{"aria-label":"Add a user",placeholder:"Search users…",autoComplete:"off"}),e.jsx(E.Trigger,{})]}),e.jsx(E.Popover,{children:e.jsx(ve,{items:p,className:"max-h-72 overflow-auto",children:s=>e.jsx(ye,{id:s.id,textValue:s.label,children:s.label})})})]})]})}const Ce=new Intl.NumberFormat(void 0,{style:"currency",currency:"USD",maximumFractionDigits:2});function D(t){return Ce.format(t)}const j=86400,q=3600,O=[{label:"No reset",seconds:null},{label:"Daily",seconds:j},{label:"Weekly",seconds:7*j},{label:"Monthly",seconds:30*j}];function we(t){if(t===null)return"No reset";const r=O.find(a=>a.seconds===t);return r?r.label:t%j===0?`Every ${t/j} days`:t%q===0?`Every ${t/q} hours`:`Every ${t}s`}function W(t){if(!t)return"—";const r=new Date(t);return Number.isNaN(r.getTime())?"—":r.toLocaleString()}function De(t){const r=t.trim();if(r==="")return{value:null,valid:!0};const a=Number(r);return!Number.isFinite(a)||a<0?{value:null,valid:!1}:{value:a,valid:!0}}function Y(t){return t!==null&&t%j===0?String(t/j):""}function Be({value:t,onChange:r,onInvalidChange:a}){const o=O.some(s=>s.seconds===t),[l,g]=i.useState(!o),[h,d]=i.useState(()=>Y(t)),p=h.trim(),c=Number(p),m=p!==""&&(!Number.isSafeInteger(c)||c<=0);return i.useEffect(()=>{a==null||a(m)},[m,a]),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Reset period"}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[O.map(s=>e.jsx(x,{size:"sm",variant:!l&&t===s.seconds?"primary":"outline",onPress:()=>{g(!1),d(Y(s.seconds)),r(s.seconds)},children:s.label},s.label)),e.jsx(x,{size:"sm",variant:l?"primary":"outline",onPress:()=>g(!0),children:"Custom"})]}),l?e.jsx("div",{className:"flex items-end gap-2",children:e.jsx(z,{label:"Every N days",value:h,onChange:s=>{d(s);const u=Number(s.trim());r(s.trim()===""||!Number.isSafeInteger(u)||u<=0?null:u*j)},placeholder:"14",description:m?e.jsx("span",{className:"text-red-700",children:"Enter a whole number of days."}):"Whole days between resets."})}):null,e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Spend returns to zero each period. A user’s clock starts when the budget is assigned to them."})]})}function G({title:t,submitLabel:r,initial:a,error:o,isPending:l,onSubmit:g,onClose:h,assignUsers:d}){const[p,c]=i.useState(a.name??""),[m,s]=i.useState(a.max_budget===null?"":String(a.max_budget)),[u,b]=i.useState(a.budget_duration_sec),[S,v]=i.useState(!1),[B,P]=i.useState([]),f=De(m),A=!l&&f.valid&&!S,C=()=>{A&&g({name:p.trim()||null,max_budget:f.value,budget_duration_sec:u},B)};return e.jsx(k,{children:e.jsxs(k.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsx("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:t}),e.jsx(T,{error:o}),e.jsx(z,{label:"Name (optional)",value:p,onChange:c,autoFocus:!0,placeholder:"team-free-tier",description:"A label to recognize this budget later."}),e.jsx(z,{label:"Spending limit (USD)",value:m,onChange:s,placeholder:"100.00",description:f.valid?"The most a single user on this budget may spend per period. Leave blank for no limit.":e.jsx("span",{className:"text-red-700",children:"Enter a non-negative number, or leave blank for no limit."})}),e.jsx(Be,{value:u,onChange:b,onInvalidChange:v}),d?e.jsx(_e,{label:"Assign to users (optional)",description:"Attach this budget to existing users now. You can also manage assignments later on the Users page.",value:B,onChange:P,users:d}):null,e.jsxs("div",{className:"flex gap-2",children:[e.jsx(x,{variant:"primary",isDisabled:!A,onPress:C,children:l?"Saving…":r}),e.jsx(x,{variant:"ghost",isDisabled:l,onPress:h,children:"Cancel"})]})]})})}function Pe({budget:t}){if(t.user_count===0)return e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"No users assigned"});const r=t.total_spend;if(t.max_budget===null)return e.jsxs("span",{className:"text-xs text-[var(--otari-ink)]",children:[D(r)," spent",e.jsx("span",{className:"text-[var(--otari-muted)]",children:" · no limit"})]});const a=t.max_budget*t.user_count,o=a>0?Math.min(100,r/a*100):0,l=r>a;return e.jsxs("div",{className:"flex min-w-[140px] flex-col gap-1",children:[e.jsxs("div",{className:"flex items-baseline justify-between gap-2 text-xs",children:[e.jsx("span",{className:"text-[var(--otari-ink)]",children:D(r)}),e.jsxs("span",{className:"text-[var(--otari-muted)]",children:["of ",D(a)]})]}),e.jsx("div",{className:"h-1.5 w-full overflow-hidden rounded-full bg-[var(--otari-line)]",role:"progressbar","aria-valuenow":Math.round(o),"aria-valuemin":0,"aria-valuemax":100,"aria-label":"Aggregate spend against total allocation",children:e.jsx("div",{className:`h-full rounded-full ${l?"bg-red-500":"bg-[var(--otari-brand)]"}`,style:{width:`${Math.max(o,l?100:2)}%`}})})]})}function Ae({budgetId:t}){const r=xe(t);if(r.isLoading)return e.jsxs("div",{className:"flex items-center gap-2 px-4 py-4 text-sm text-[var(--otari-muted)]",children:[e.jsx(Ne,{size:"sm"})," Loading reset history…"]});if(r.error)return e.jsx("div",{className:"px-4 py-4",children:e.jsx(T,{error:r.error})});const a=r.data??[];return a.length===0?e.jsx("div",{className:"px-4 py-4 text-sm text-[var(--otari-muted)]",children:"No resets recorded yet for this budget."}):e.jsx("div",{className:"overflow-x-auto px-4 py-3",children:e.jsxs("table",{className:"w-full border-collapse text-xs",children:[e.jsx("thead",{className:"text-left text-[var(--otari-muted)]",children:e.jsxs("tr",{children:[e.jsx("th",{className:"py-1.5 pr-4 font-medium",children:"User"}),e.jsx("th",{className:"py-1.5 pr-4 font-medium",children:"Spend cleared"}),e.jsx("th",{className:"py-1.5 pr-4 font-medium",children:"Reset at"}),e.jsx("th",{className:"py-1.5 font-medium",children:"Next reset"})]})}),e.jsx("tbody",{children:a.map(o=>e.jsxs("tr",{className:"border-t border-[var(--otari-line)]",children:[e.jsx("td",{className:"py-1.5 pr-4",children:e.jsx("code",{children:o.user_id??"—"})}),e.jsx("td",{className:"py-1.5 pr-4 text-[var(--otari-ink)]",children:D(o.previous_spend)}),e.jsx("td",{className:"py-1.5 pr-4 text-[var(--otari-muted)]",children:W(o.reset_at)}),e.jsx("td",{className:"py-1.5 text-[var(--otari-muted)]",children:W(o.next_reset_at)})]},o.id))})]})})}function Ee({label:t,isPending:r,onConfirm:a}){const[o,l]=i.useState(!1);return o?e.jsxs("div",{className:"flex flex-col items-end gap-1.5 rounded-lg border border-amber-200 bg-amber-50 p-2 text-right",children:[e.jsxs("span",{className:"max-w-xs text-xs text-amber-800",children:["Delete ",e.jsx("strong",{children:t}),"? Users keep their spend but lose this limit. Cannot be undone."]}),e.jsxs("span",{className:"inline-flex gap-1",children:[e.jsx(x,{size:"sm",variant:"danger",isDisabled:r,onPress:a,children:"Delete permanently"}),e.jsx(x,{size:"sm",variant:"ghost",isDisabled:r,onPress:()=>l(!1),children:"Cancel"})]})]}):e.jsx(x,{size:"sm",variant:"danger-soft",onPress:()=>l(!0),children:"Delete"})}const ke=t=>t.budget_id;function Q(t){return t.split("-")[0]}function $(t){return t.name??Q(t.budget_id)}function Te(){const t=ae(),r=re(),a=le(),o=ie(),l=oe(),g=de(),[h,d]=i.useState(!1),[p,c]=i.useState(null),[m,s]=i.useState(null),[u,b]=i.useState(null),[S,v]=i.useState(null),[B,P]=i.useState(!1),f=ge(),[A,C]=i.useState(!1),[X,F]=i.useState(void 0),[J,H]=i.useState(!1),w=t.data??[],K=t.isLoading,y=w.find(n=>n.budget_id===p)??null,U=w.find(n=>n.budget_id===m)??null,L=!K&&w.length===0&&!h,Z=w.map(n=>n.budget_id),_=he(f.selectedKeys,Z),ee=async()=>{H(!0),F(void 0);try{for(const n of _)await l.mutateAsync(n);f.clear(),C(!1)}catch(n){F(n)}finally{H(!1)}},te=i.useMemo(()=>[{id:"budget",header:"Budget",isRowHeader:!0,cell:n=>e.jsxs("div",{className:"flex flex-col gap-0.5",children:[e.jsx("span",{className:"font-medium text-[var(--otari-ink)]",children:n.name??e.jsx("span",{className:"text-[var(--otari-muted)]",children:"(unnamed)"})}),e.jsx("code",{className:"text-[11px] text-[var(--otari-muted)]",title:n.budget_id,children:Q(n.budget_id)})]})},{id:"limit",header:"Limit (per user)",cell:n=>n.max_budget===null?e.jsx("span",{className:"text-[var(--otari-muted)]",children:"Unlimited"}):D(n.max_budget)},{id:"reset",header:"Reset",cell:n=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:we(n.budget_duration_sec)})},{id:"users",header:"Users",cell:n=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:n.user_count})},{id:"usage",header:"Usage",cell:n=>e.jsx(Pe,{budget:n})},{id:"actions",header:"Actions",align:"end",cell:n=>e.jsxs("div",{className:"flex items-center justify-end gap-1.5",children:[e.jsx(x,{size:"sm",variant:"ghost",onPress:()=>s(N=>N===n.budget_id?null:n.budget_id),children:m===n.budget_id?"Hide history":"History"}),e.jsx(x,{size:"sm",variant:"ghost",onPress:()=>{d(!1),c(n.budget_id)},children:"Edit"}),e.jsx(Ee,{label:$(n),isPending:l.isPending,onConfirm:()=>l.mutate(n.budget_id)})]})}],[m,l.isPending,l.mutate]),V=async(n,N)=>{P(!0),b(null);const I=await Promise.allSettled(N.map(M=>g.mutateAsync({id:M,body:{budget_id:n}})));P(!1);const R=I.flatMap((M,ne)=>M.status==="rejected"?[N[ne]]:[]);if(R.length>0){v({budgetId:n,userIds:R}),b(new Error(`Budget created, but could not assign it to: ${R.join(", ")}. Retry to try again.`));return}v(null),d(!1)},se=(n,N)=>{if(S){V(S.budgetId,S.userIds);return}b(null),a.mutate(n,{onSuccess:async I=>{if(N.length>0){await V(I.budget_id,N);return}d(!1)}})};return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(ue,{title:"Budgets",description:"Define spending limits and reset schedules. Assign a budget to users to enforce it.",action:h||L?null:e.jsx(x,{variant:"primary",onPress:()=>{c(null),b(null),v(null),d(!0)},children:"Create budget"})}),e.jsx(T,{error:t.error??a.error??o.error??l.error??g.error}),e.jsx(ce,{children:"Assign a budget to users when you create it, or later from the Users page. Each row’s usage aggregates the spend of the users currently on that budget."}),L?e.jsx(me,{title:"No budgets yet",description:"A budget caps how much a user may spend and, optionally, resets that spend on a schedule. Create one, then assign it to users to enforce a limit.",actionLabel:"Create your first budget",onAction:()=>{c(null),b(null),v(null),d(!0)}}):null,h?e.jsx(G,{title:"Create budget",submitLabel:S?"Retry assignments":"Create budget",initial:{name:null,max_budget:null,budget_duration_sec:null},error:a.error??u,isPending:a.isPending||B,assignUsers:r.data??[],onSubmit:se,onClose:()=>{b(null),v(null),d(!1)}}):null,y?e.jsx(G,{title:`Edit budget ${$(y)}`,submitLabel:"Save changes",initial:{name:y.name,max_budget:y.max_budget,budget_duration_sec:y.budget_duration_sec},error:o.error,isPending:o.isPending,onSubmit:n=>o.mutate({id:y.budget_id,body:n},{onSuccess:()=>c(null)}),onClose:()=>c(null)},y.budget_id):null,_.length>0?e.jsx(pe,{selectedCount:_.length,allMatching:!1,matchingTotal:null,canSelectAllMatching:!1,onSelectAllMatching:()=>{},onClear:f.clear,children:e.jsx(x,{size:"sm",variant:"danger",onPress:()=>C(!0),children:"Delete"})}):null,L?null:e.jsx(be,{ariaLabel:"Budgets",columns:te,rows:w,getRowKey:ke,isLoading:K,emptyContent:"No budgets yet. Create one to cap spending.",selectionMode:"multiple",selectedKeys:f.selectedKeys,onSelectionChange:f.onSelectionChange}),U?e.jsx(k,{children:e.jsxs(k.Content,{className:"p-0",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-[var(--otari-line)] px-4 py-2",children:[e.jsxs("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:["Reset history — ",$(U)]}),e.jsx(x,{size:"sm",variant:"ghost",onPress:()=>s(null),children:"Close"})]}),e.jsx(Ae,{budgetId:U.budget_id})]})}):null,e.jsx(fe,{isOpen:A,onOpenChange:C,heading:"Delete budgets",body:`Delete ${_.length} ${_.length===1?"budget":"budgets"}? Users on ${_.length===1?"it":"them"} will no longer be capped.`,confirmLabel:"Delete",isPending:J,error:X,onConfirm:ee})]})}export{Te as BudgetsPage}; +import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as i}from"./react-dgEcD0HR.js";import{q as ae,u as re,r as le,s as ie,t as oe,v as de,P as ue,E as T,I as ce,w as me,x as xe}from"./index-Br3lxdY5.js";import{u as ge,r as he,B as pe}from"./tableSelection-9hV37uhu.js";import{C as fe}from"./ConfirmDialog-KQrh5qGO.js";import{D as be}from"./DataTable-Bmn_eYSV.js";import{F as z}from"./Field-Dwp6u3t8.js";import{C as E,I as je,a as ve,b as ye,B as x,d as k,S as Ne}from"./heroui-BI50yK5B.js";const Se=50;function _e({value:t,onChange:r,users:a,label:o,description:l}){const[g,h]=i.useState(""),d=i.useMemo(()=>a.filter(s=>!s.user_id.startsWith("apikey-")).map(s=>({id:s.user_id,label:s.alias?`${s.user_id} (${s.alias})`:s.user_id})),[a]),p=i.useMemo(()=>{const s=g.trim().toLowerCase();return d.filter(u=>!t.includes(u.id)).filter(u=>!s||u.id.toLowerCase().includes(s)||u.label.toLowerCase().includes(s)).slice(0,Se)},[d,t,g]),c=s=>{t.includes(s)||r([...t,s]),h("")},m=s=>r(t.filter(u=>u!==s));return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{children:[e.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:o}),l?e.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:l}):null]}),t.length>0?e.jsx("div",{className:"flex flex-wrap gap-1.5",children:t.map(s=>e.jsxs("span",{className:"inline-flex items-center gap-1 rounded-full bg-[var(--otari-brand-tint)] px-2.5 py-1 font-mono text-xs text-[var(--otari-brand-dark)]",children:[s,e.jsx("button",{type:"button","aria-label":`Remove ${s}`,onClick:()=>m(s),className:"text-[var(--otari-brand-dark)] hover:text-red-700",children:"×"})]},s))}):null,d.length===0?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"No users yet. Create users first, then assign them here or from the Users page."}):e.jsxs(E.Root,{allowsEmptyCollection:!0,menuTrigger:"input",inputValue:g,onInputChange:h,selectedKey:null,onSelectionChange:s=>{s!=null&&c(String(s))},className:"flex flex-col gap-1",children:[e.jsxs(E.InputGroup,{children:[e.jsx(je,{"aria-label":"Add a user",placeholder:"Search users…",autoComplete:"off"}),e.jsx(E.Trigger,{})]}),e.jsx(E.Popover,{children:e.jsx(ve,{items:p,className:"max-h-72 overflow-auto",children:s=>e.jsx(ye,{id:s.id,textValue:s.label,children:s.label})})})]})]})}const Ce=new Intl.NumberFormat(void 0,{style:"currency",currency:"USD",maximumFractionDigits:2});function D(t){return Ce.format(t)}const j=86400,q=3600,O=[{label:"No reset",seconds:null},{label:"Daily",seconds:j},{label:"Weekly",seconds:7*j},{label:"Monthly",seconds:30*j}];function we(t){if(t===null)return"No reset";const r=O.find(a=>a.seconds===t);return r?r.label:t%j===0?`Every ${t/j} days`:t%q===0?`Every ${t/q} hours`:`Every ${t}s`}function W(t){if(!t)return"—";const r=new Date(t);return Number.isNaN(r.getTime())?"—":r.toLocaleString()}function De(t){const r=t.trim();if(r==="")return{value:null,valid:!0};const a=Number(r);return!Number.isFinite(a)||a<0?{value:null,valid:!1}:{value:a,valid:!0}}function Y(t){return t!==null&&t%j===0?String(t/j):""}function Be({value:t,onChange:r,onInvalidChange:a}){const o=O.some(s=>s.seconds===t),[l,g]=i.useState(!o),[h,d]=i.useState(()=>Y(t)),p=h.trim(),c=Number(p),m=p!==""&&(!Number.isSafeInteger(c)||c<=0);return i.useEffect(()=>{a==null||a(m)},[m,a]),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Reset period"}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[O.map(s=>e.jsx(x,{size:"sm",variant:!l&&t===s.seconds?"primary":"outline",onPress:()=>{g(!1),d(Y(s.seconds)),r(s.seconds)},children:s.label},s.label)),e.jsx(x,{size:"sm",variant:l?"primary":"outline",onPress:()=>g(!0),children:"Custom"})]}),l?e.jsx("div",{className:"flex items-end gap-2",children:e.jsx(z,{label:"Every N days",value:h,onChange:s=>{d(s);const u=Number(s.trim());r(s.trim()===""||!Number.isSafeInteger(u)||u<=0?null:u*j)},placeholder:"14",description:m?e.jsx("span",{className:"text-red-700",children:"Enter a whole number of days."}):"Whole days between resets."})}):null,e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Spend returns to zero each period. A user’s clock starts when the budget is assigned to them."})]})}function G({title:t,submitLabel:r,initial:a,error:o,isPending:l,onSubmit:g,onClose:h,assignUsers:d}){const[p,c]=i.useState(a.name??""),[m,s]=i.useState(a.max_budget===null?"":String(a.max_budget)),[u,b]=i.useState(a.budget_duration_sec),[S,v]=i.useState(!1),[B,P]=i.useState([]),f=De(m),A=!l&&f.valid&&!S,C=()=>{A&&g({name:p.trim()||null,max_budget:f.value,budget_duration_sec:u},B)};return e.jsx(k,{children:e.jsxs(k.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsx("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:t}),e.jsx(T,{error:o}),e.jsx(z,{label:"Name (optional)",value:p,onChange:c,autoFocus:!0,placeholder:"team-free-tier",description:"A label to recognize this budget later."}),e.jsx(z,{label:"Spending limit (USD)",value:m,onChange:s,placeholder:"100.00",description:f.valid?"The most a single user on this budget may spend per period. Leave blank for no limit.":e.jsx("span",{className:"text-red-700",children:"Enter a non-negative number, or leave blank for no limit."})}),e.jsx(Be,{value:u,onChange:b,onInvalidChange:v}),d?e.jsx(_e,{label:"Assign to users (optional)",description:"Attach this budget to existing users now. You can also manage assignments later on the Users page.",value:B,onChange:P,users:d}):null,e.jsxs("div",{className:"flex gap-2",children:[e.jsx(x,{variant:"primary",isDisabled:!A,onPress:C,children:l?"Saving…":r}),e.jsx(x,{variant:"ghost",isDisabled:l,onPress:h,children:"Cancel"})]})]})})}function Pe({budget:t}){if(t.user_count===0)return e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"No users assigned"});const r=t.total_spend;if(t.max_budget===null)return e.jsxs("span",{className:"text-xs text-[var(--otari-ink)]",children:[D(r)," spent",e.jsx("span",{className:"text-[var(--otari-muted)]",children:" · no limit"})]});const a=t.max_budget*t.user_count,o=a>0?Math.min(100,r/a*100):0,l=r>a;return e.jsxs("div",{className:"flex min-w-[140px] flex-col gap-1",children:[e.jsxs("div",{className:"flex items-baseline justify-between gap-2 text-xs",children:[e.jsx("span",{className:"text-[var(--otari-ink)]",children:D(r)}),e.jsxs("span",{className:"text-[var(--otari-muted)]",children:["of ",D(a)]})]}),e.jsx("div",{className:"h-1.5 w-full overflow-hidden rounded-full bg-[var(--otari-line)]",role:"progressbar","aria-valuenow":Math.round(o),"aria-valuemin":0,"aria-valuemax":100,"aria-label":"Aggregate spend against total allocation",children:e.jsx("div",{className:`h-full rounded-full ${l?"bg-red-500":"bg-[var(--otari-brand)]"}`,style:{width:`${Math.max(o,l?100:2)}%`}})})]})}function Ae({budgetId:t}){const r=xe(t);if(r.isLoading)return e.jsxs("div",{className:"flex items-center gap-2 px-4 py-4 text-sm text-[var(--otari-muted)]",children:[e.jsx(Ne,{size:"sm"})," Loading reset history…"]});if(r.error)return e.jsx("div",{className:"px-4 py-4",children:e.jsx(T,{error:r.error})});const a=r.data??[];return a.length===0?e.jsx("div",{className:"px-4 py-4 text-sm text-[var(--otari-muted)]",children:"No resets recorded yet for this budget."}):e.jsx("div",{className:"overflow-x-auto px-4 py-3",children:e.jsxs("table",{className:"w-full border-collapse text-xs",children:[e.jsx("thead",{className:"text-left text-[var(--otari-muted)]",children:e.jsxs("tr",{children:[e.jsx("th",{className:"py-1.5 pr-4 font-medium",children:"User"}),e.jsx("th",{className:"py-1.5 pr-4 font-medium",children:"Spend cleared"}),e.jsx("th",{className:"py-1.5 pr-4 font-medium",children:"Reset at"}),e.jsx("th",{className:"py-1.5 font-medium",children:"Next reset"})]})}),e.jsx("tbody",{children:a.map(o=>e.jsxs("tr",{className:"border-t border-[var(--otari-line)]",children:[e.jsx("td",{className:"py-1.5 pr-4",children:e.jsx("code",{children:o.user_id??"—"})}),e.jsx("td",{className:"py-1.5 pr-4 text-[var(--otari-ink)]",children:D(o.previous_spend)}),e.jsx("td",{className:"py-1.5 pr-4 text-[var(--otari-muted)]",children:W(o.reset_at)}),e.jsx("td",{className:"py-1.5 text-[var(--otari-muted)]",children:W(o.next_reset_at)})]},o.id))})]})})}function Ee({label:t,isPending:r,onConfirm:a}){const[o,l]=i.useState(!1);return o?e.jsxs("div",{className:"flex flex-col items-end gap-1.5 rounded-lg border border-amber-200 bg-amber-50 p-2 text-right",children:[e.jsxs("span",{className:"max-w-xs text-xs text-amber-800",children:["Delete ",e.jsx("strong",{children:t}),"? Users keep their spend but lose this limit. Cannot be undone."]}),e.jsxs("span",{className:"inline-flex gap-1",children:[e.jsx(x,{size:"sm",variant:"danger",isDisabled:r,onPress:a,children:"Delete permanently"}),e.jsx(x,{size:"sm",variant:"ghost",isDisabled:r,onPress:()=>l(!1),children:"Cancel"})]})]}):e.jsx(x,{size:"sm",variant:"danger-soft",onPress:()=>l(!0),children:"Delete"})}const ke=t=>t.budget_id;function Q(t){return t.split("-")[0]}function $(t){return t.name??Q(t.budget_id)}function Te(){const t=ae(),r=re(),a=le(),o=ie(),l=oe(),g=de(),[h,d]=i.useState(!1),[p,c]=i.useState(null),[m,s]=i.useState(null),[u,b]=i.useState(null),[S,v]=i.useState(null),[B,P]=i.useState(!1),f=ge(),[A,C]=i.useState(!1),[X,F]=i.useState(void 0),[J,H]=i.useState(!1),w=t.data??[],K=t.isLoading,y=w.find(n=>n.budget_id===p)??null,U=w.find(n=>n.budget_id===m)??null,L=!K&&w.length===0&&!h,Z=w.map(n=>n.budget_id),_=he(f.selectedKeys,Z),ee=async()=>{H(!0),F(void 0);try{for(const n of _)await l.mutateAsync(n);f.clear(),C(!1)}catch(n){F(n)}finally{H(!1)}},te=i.useMemo(()=>[{id:"budget",header:"Budget",isRowHeader:!0,cell:n=>e.jsxs("div",{className:"flex flex-col gap-0.5",children:[e.jsx("span",{className:"font-medium text-[var(--otari-ink)]",children:n.name??e.jsx("span",{className:"text-[var(--otari-muted)]",children:"(unnamed)"})}),e.jsx("code",{className:"text-[11px] text-[var(--otari-muted)]",title:n.budget_id,children:Q(n.budget_id)})]})},{id:"limit",header:"Limit (per user)",cell:n=>n.max_budget===null?e.jsx("span",{className:"text-[var(--otari-muted)]",children:"Unlimited"}):D(n.max_budget)},{id:"reset",header:"Reset",cell:n=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:we(n.budget_duration_sec)})},{id:"users",header:"Users",cell:n=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:n.user_count})},{id:"usage",header:"Usage",cell:n=>e.jsx(Pe,{budget:n})},{id:"actions",header:"Actions",align:"end",cell:n=>e.jsxs("div",{className:"flex items-center justify-end gap-1.5",children:[e.jsx(x,{size:"sm",variant:"ghost",onPress:()=>s(N=>N===n.budget_id?null:n.budget_id),children:m===n.budget_id?"Hide history":"History"}),e.jsx(x,{size:"sm",variant:"ghost",onPress:()=>{d(!1),c(n.budget_id)},children:"Edit"}),e.jsx(Ee,{label:$(n),isPending:l.isPending,onConfirm:()=>l.mutate(n.budget_id)})]})}],[m,l.isPending,l.mutate]),V=async(n,N)=>{P(!0),b(null);const I=await Promise.allSettled(N.map(M=>g.mutateAsync({id:M,body:{budget_id:n}})));P(!1);const R=I.flatMap((M,ne)=>M.status==="rejected"?[N[ne]]:[]);if(R.length>0){v({budgetId:n,userIds:R}),b(new Error(`Budget created, but could not assign it to: ${R.join(", ")}. Retry to try again.`));return}v(null),d(!1)},se=(n,N)=>{if(S){V(S.budgetId,S.userIds);return}b(null),a.mutate(n,{onSuccess:async I=>{if(N.length>0){await V(I.budget_id,N);return}d(!1)}})};return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(ue,{title:"Budgets",description:"Define spending limits and reset schedules. Assign a budget to users to enforce it.",action:h||L?null:e.jsx(x,{variant:"primary",onPress:()=>{c(null),b(null),v(null),d(!0)},children:"Create budget"})}),e.jsx(T,{error:t.error??a.error??o.error??l.error??g.error}),e.jsx(ce,{children:"Assign a budget to users when you create it, or later from the Users page. Each row’s usage aggregates the spend of the users currently on that budget."}),L?e.jsx(me,{title:"No budgets yet",description:"A budget caps how much a user may spend and, optionally, resets that spend on a schedule. Create one, then assign it to users to enforce a limit.",actionLabel:"Create your first budget",onAction:()=>{c(null),b(null),v(null),d(!0)}}):null,h?e.jsx(G,{title:"Create budget",submitLabel:S?"Retry assignments":"Create budget",initial:{name:null,max_budget:null,budget_duration_sec:null},error:a.error??u,isPending:a.isPending||B,assignUsers:r.data??[],onSubmit:se,onClose:()=>{b(null),v(null),d(!1)}}):null,y?e.jsx(G,{title:`Edit budget ${$(y)}`,submitLabel:"Save changes",initial:{name:y.name,max_budget:y.max_budget,budget_duration_sec:y.budget_duration_sec},error:o.error,isPending:o.isPending,onSubmit:n=>o.mutate({id:y.budget_id,body:n},{onSuccess:()=>c(null)}),onClose:()=>c(null)},y.budget_id):null,_.length>0?e.jsx(pe,{selectedCount:_.length,allMatching:!1,matchingTotal:null,canSelectAllMatching:!1,onSelectAllMatching:()=>{},onClear:f.clear,children:e.jsx(x,{size:"sm",variant:"danger",onPress:()=>C(!0),children:"Delete"})}):null,L?null:e.jsx(be,{ariaLabel:"Budgets",columns:te,rows:w,getRowKey:ke,isLoading:K,emptyContent:"No budgets yet. Create one to cap spending.",selectionMode:"multiple",selectedKeys:f.selectedKeys,onSelectionChange:f.onSelectionChange}),U?e.jsx(k,{children:e.jsxs(k.Content,{className:"p-0",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-[var(--otari-line)] px-4 py-2",children:[e.jsxs("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:["Reset history — ",$(U)]}),e.jsx(x,{size:"sm",variant:"ghost",onPress:()=>s(null),children:"Close"})]}),e.jsx(Ae,{budgetId:U.budget_id})]})}):null,e.jsx(fe,{isOpen:A,onOpenChange:C,heading:"Delete budgets",body:`Delete ${_.length} ${_.length===1?"budget":"budgets"}? Users on ${_.length===1?"it":"them"} will no longer be capped.`,confirmLabel:"Delete",isPending:J,error:X,onConfirm:ee})]})}export{Te as BudgetsPage}; diff --git a/src/gateway/static/dashboard/assets/ConfirmDialog-KvA7b1-a.js b/src/gateway/static/dashboard/assets/ConfirmDialog-KQrh5qGO.js similarity index 92% rename from src/gateway/static/dashboard/assets/ConfirmDialog-KvA7b1-a.js rename to src/gateway/static/dashboard/assets/ConfirmDialog-KQrh5qGO.js index 97073b50..d814db3f 100644 --- a/src/gateway/static/dashboard/assets/ConfirmDialog-KvA7b1-a.js +++ b/src/gateway/static/dashboard/assets/ConfirmDialog-KQrh5qGO.js @@ -1 +1 @@ -import{j as r}from"./tanstack-query-1t81HyiD.js";import{E as j}from"./index-DV8qvdUj.js";import{A as e,B as l}from"./heroui-BI50yK5B.js";function p({isOpen:s,onOpenChange:a,heading:n,body:o,confirmLabel:t,confirmVariant:c="danger",isPending:i,error:d,onConfirm:x}){return r.jsx(e,{isOpen:s,onOpenChange:a,children:s?r.jsx(e.Backdrop,{children:r.jsx(e.Container,{placement:"center",size:"md",children:r.jsxs(e.Dialog,{children:[r.jsx(e.Header,{children:r.jsx(e.Heading,{children:n})}),r.jsxs(e.Body,{className:"flex flex-col gap-4",children:[r.jsx("div",{className:"text-sm text-[var(--otari-muted)]",children:o}),r.jsx(j,{error:d})]}),r.jsxs(e.Footer,{children:[r.jsx(l,{variant:"ghost",isDisabled:i,onPress:()=>a(!1),children:"Cancel"}),r.jsx(l,{variant:c,isPending:i,onPress:x,children:t})]})]})})}):null})}export{p as C}; +import{j as r}from"./tanstack-query-1t81HyiD.js";import{E as j}from"./index-Br3lxdY5.js";import{A as e,B as l}from"./heroui-BI50yK5B.js";function p({isOpen:s,onOpenChange:a,heading:n,body:o,confirmLabel:t,confirmVariant:c="danger",isPending:i,error:d,onConfirm:x}){return r.jsx(e,{isOpen:s,onOpenChange:a,children:s?r.jsx(e.Backdrop,{children:r.jsx(e.Container,{placement:"center",size:"md",children:r.jsxs(e.Dialog,{children:[r.jsx(e.Header,{children:r.jsx(e.Heading,{children:n})}),r.jsxs(e.Body,{className:"flex flex-col gap-4",children:[r.jsx("div",{className:"text-sm text-[var(--otari-muted)]",children:o}),r.jsx(j,{error:d})]}),r.jsxs(e.Footer,{children:[r.jsx(l,{variant:"ghost",isDisabled:i,onPress:()=>a(!1),children:"Cancel"}),r.jsx(l,{variant:c,isPending:i,onPress:x,children:t})]})]})})}):null})}export{p as C}; diff --git a/src/gateway/static/dashboard/assets/DocsPage-BZcIfS8u.js b/src/gateway/static/dashboard/assets/DocsPage-Bo_X9aMz.js similarity index 95% rename from src/gateway/static/dashboard/assets/DocsPage-BZcIfS8u.js rename to src/gateway/static/dashboard/assets/DocsPage-Bo_X9aMz.js index 81d718bc..2bef3714 100644 --- a/src/gateway/static/dashboard/assets/DocsPage-BZcIfS8u.js +++ b/src/gateway/static/dashboard/assets/DocsPage-Bo_X9aMz.js @@ -1,32 +1,32 @@ -import{j as re}from"./tanstack-query-1t81HyiD.js";import{P as gi}from"./index-DV8qvdUj.js";import{d as ct}from"./heroui-BI50yK5B.js";import{g as nr}from"./react-dgEcD0HR.js";function yi(e,t){const n={};return(e[e.length-1]===""?[...e,""]:e).join((n.padRight?" ":"")+","+(n.padLeft===!1?"":" ")).trim()}const ki=/^[$_\p{ID_Start}][$_\u{200C}\u{200D}\p{ID_Continue}]*$/u,xi=/^[$_\p{ID_Start}][-$_\u{200C}\u{200D}\p{ID_Continue}]*$/u,bi={};function ft(e,t){return(bi.jsx?xi:ki).test(e)}const wi=/[ \t\n\f\r]/g;function Si(e){return typeof e=="object"?e.type==="text"?ht(e.value):!1:ht(e)}function ht(e){return e.replace(wi,"")===""}class Ke{constructor(t,n,r){this.normal=n,this.property=t,r&&(this.space=r)}}Ke.prototype.normal={};Ke.prototype.property={};Ke.prototype.space=void 0;function tr(e,t){const n={},r={};for(const i of e)Object.assign(n,i.property),Object.assign(r,i.normal);return new Ke(n,r,t)}function zn(e){return e.toLowerCase()}class ee{constructor(t,n){this.attribute=n,this.property=t}}ee.prototype.attribute="";ee.prototype.booleanish=!1;ee.prototype.boolean=!1;ee.prototype.commaOrSpaceSeparated=!1;ee.prototype.commaSeparated=!1;ee.prototype.defined=!1;ee.prototype.mustUseProperty=!1;ee.prototype.number=!1;ee.prototype.overloadedBoolean=!1;ee.prototype.property="";ee.prototype.spaceSeparated=!1;ee.prototype.space=void 0;let Ci=0;const L=Ae(),Y=Ae(),Dn=Ae(),E=Ae(),$=Ae(),Ie=Ae(),te=Ae();function Ae(){return 2**++Ci}const Ln=Object.freeze(Object.defineProperty({__proto__:null,boolean:L,booleanish:Y,commaOrSpaceSeparated:te,commaSeparated:Ie,number:E,overloadedBoolean:Dn,spaceSeparated:$},Symbol.toStringTag,{value:"Module"})),pn=Object.keys(Ln);class Hn extends ee{constructor(t,n,r,i){let o=-1;if(super(t,n),pt(this,"space",i),typeof r=="number")for(;++o4&&n.slice(0,4)==="data"&&vi.test(t)){if(t.charAt(4)==="-"){const o=t.slice(5).replace(mt,Di);r="data"+o.charAt(0).toUpperCase()+o.slice(1)}else{const o=t.slice(4);if(!mt.test(o)){let l=o.replace(Ai,zi);l.charAt(0)!=="-"&&(l="-"+l),t="data"+l}}i=Hn}return new i(r,t)}function zi(e){return"-"+e.toLowerCase()}function Di(e){return e.charAt(1).toUpperCase()}const Li=tr([rr,Ei,or,ar,ur],"html"),Un=tr([rr,Ii,or,ar,ur],"svg");function Fi(e){return e.join(" ").trim()}var De={},mn,dt;function Ri(){if(dt)return mn;dt=1;var e=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//g,t=/\n/g,n=/^\s*/,r=/^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/,i=/^:\s*/,o=/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/,l=/^[;\s]*/,a=/^\s+|\s+$/g,u=` -`,s="/",f="*",c="",p="comment",h="declaration";function g(S,y){if(typeof S!="string")throw new TypeError("First argument must be a string");if(!S)return[];y=y||{};var I=1,C=1;function R(D){var A=D.match(t);A&&(I+=A.length);var q=D.lastIndexOf(u);C=~q?D.length-q:C+D.length}function _(){var D={line:I,column:C};return function(A){return A.position=new b(D),j(),A}}function b(D){this.start=D,this.end={line:I,column:C},this.source=y.source}b.prototype.content=S;function M(D){var A=new Error(y.source+":"+I+":"+C+": "+D);if(A.reason=D,A.filename=y.source,A.line=I,A.column=C,A.source=S,!y.silent)throw A}function H(D){var A=D.exec(S);if(A){var q=A[0];return R(q),S=S.slice(q.length),A}}function j(){H(n)}function k(D){var A;for(D=D||[];A=v();)A!==!1&&D.push(A);return D}function v(){var D=_();if(!(s!=S.charAt(0)||f!=S.charAt(1))){for(var A=2;c!=S.charAt(A)&&(f!=S.charAt(A)||s!=S.charAt(A+1));)++A;if(A+=2,c===S.charAt(A-1))return M("End of comment missing");var q=S.slice(2,A-2);return C+=2,R(q),S=S.slice(A),C+=2,D({type:p,comment:q})}}function P(){var D=_(),A=H(r);if(A){if(v(),!H(i))return M("property missing ':'");var q=H(o),K=D({type:h,property:w(A[0].replace(e,c)),value:q?w(q[0].replace(e,c)):c});return H(l),K}}function U(){var D=[];k(D);for(var A;A=P();)A!==!1&&(D.push(A),k(D));return D}return j(),U()}function w(S){return S?S.replace(a,c):c}return mn=g,mn}var gt;function _i(){if(gt)return De;gt=1;var e=De&&De.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(De,"__esModule",{value:!0}),De.default=n;const t=e(Ri());function n(r,i){let o=null;if(!r||typeof r!="string")return o;const l=(0,t.default)(r),a=typeof i=="function";return l.forEach(u=>{if(u.type!=="declaration")return;const{property:s,value:f}=u;a?i(s,f,u):f&&(o=o||{},o[s]=f)}),o}return De}var Be={},yt;function Oi(){if(yt)return Be;yt=1,Object.defineProperty(Be,"__esModule",{value:!0}),Be.camelCase=void 0;var e=/^--[a-zA-Z0-9_-]+$/,t=/-([a-z])/g,n=/^[^-]+$/,r=/^-(webkit|moz|ms|o|khtml)-/,i=/^-(ms)-/,o=function(s){return!s||n.test(s)||e.test(s)},l=function(s,f){return f.toUpperCase()},a=function(s,f){return"".concat(f,"-")},u=function(s,f){return f===void 0&&(f={}),o(s)?s:(s=s.toLowerCase(),f.reactCompat?s=s.replace(i,a):s=s.replace(r,a),s.replace(t,l))};return Be.camelCase=u,Be}var je,kt;function Mi(){if(kt)return je;kt=1;var e=je&&je.__importDefault||function(i){return i&&i.__esModule?i:{default:i}},t=e(_i()),n=Oi();function r(i,o){var l={};return!i||typeof i!="string"||(0,t.default)(i,function(a,u){a&&u&&(l[(0,n.camelCase)(a,o)]=u)}),l}return r.default=r,je=r,je}var Ni=Mi();const Bi=nr(Ni),sr=cr("end"),qn=cr("start");function cr(e){return t;function t(n){const r=n&&n.position&&n.position[e]||{};if(typeof r.line=="number"&&r.line>0&&typeof r.column=="number"&&r.column>0)return{line:r.line,column:r.column,offset:typeof r.offset=="number"&&r.offset>-1?r.offset:void 0}}}function ji(e){const t=qn(e),n=sr(e);if(t&&n)return{start:t,end:n}}function qe(e){return!e||typeof e!="object"?"":"position"in e||"type"in e?xt(e.position):"start"in e||"end"in e?xt(e):"line"in e||"column"in e?Fn(e):""}function Fn(e){return bt(e&&e.line)+":"+bt(e&&e.column)}function xt(e){return Fn(e&&e.start)+"-"+Fn(e&&e.end)}function bt(e){return e&&typeof e=="number"?e:1}class G extends Error{constructor(t,n,r){super(),typeof n=="string"&&(r=n,n=void 0);let i="",o={},l=!1;if(n&&("line"in n&&"column"in n?o={place:n}:"start"in n&&"end"in n?o={place:n}:"type"in n?o={ancestors:[n],place:n.position}:o={...n}),typeof t=="string"?i=t:!o.cause&&t&&(l=!0,i=t.message,o.cause=t),!o.ruleId&&!o.source&&typeof r=="string"){const u=r.indexOf(":");u===-1?o.ruleId=r:(o.source=r.slice(0,u),o.ruleId=r.slice(u+1))}if(!o.place&&o.ancestors&&o.ancestors){const u=o.ancestors[o.ancestors.length-1];u&&(o.place=u.position)}const a=o.place&&"start"in o.place?o.place.start:o.place;this.ancestors=o.ancestors||void 0,this.cause=o.cause||void 0,this.column=a?a.column:void 0,this.fatal=void 0,this.file="",this.message=i,this.line=a?a.line:void 0,this.name=qe(o.place)||"1:1",this.place=o.place||void 0,this.reason=this.message,this.ruleId=o.ruleId||void 0,this.source=o.source||void 0,this.stack=l&&o.cause&&typeof o.cause.stack=="string"?o.cause.stack:"",this.actual=void 0,this.expected=void 0,this.note=void 0,this.url=void 0}}G.prototype.file="";G.prototype.name="";G.prototype.reason="";G.prototype.message="";G.prototype.stack="";G.prototype.column=void 0;G.prototype.line=void 0;G.prototype.ancestors=void 0;G.prototype.cause=void 0;G.prototype.fatal=void 0;G.prototype.place=void 0;G.prototype.ruleId=void 0;G.prototype.source=void 0;const Vn={}.hasOwnProperty,Hi=new Map,Ui=/[A-Z]/g,qi=new Set(["table","tbody","thead","tfoot","tr"]),Vi=new Set(["td","th"]),fr="https://github.com/syntax-tree/hast-util-to-jsx-runtime";function $i(e,t){if(!t||t.Fragment===void 0)throw new TypeError("Expected `Fragment` in options");const n=t.filePath||void 0;let r;if(t.development){if(typeof t.jsxDEV!="function")throw new TypeError("Expected `jsxDEV` in options when `development: true`");r=Zi(n,t.jsxDEV)}else{if(typeof t.jsx!="function")throw new TypeError("Expected `jsx` in production options");if(typeof t.jsxs!="function")throw new TypeError("Expected `jsxs` in production options");r=Ji(n,t.jsx,t.jsxs)}const i={Fragment:t.Fragment,ancestors:[],components:t.components||{},create:r,elementAttributeNameCase:t.elementAttributeNameCase||"react",evaluater:t.createEvaluater?t.createEvaluater():void 0,filePath:n,ignoreInvalidStyle:t.ignoreInvalidStyle||!1,passKeys:t.passKeys!==!1,passNode:t.passNode||!1,schema:t.space==="svg"?Un:Li,stylePropertyNameCase:t.stylePropertyNameCase||"dom",tableCellAlignToStyle:t.tableCellAlignToStyle!==!1},o=hr(i,e,void 0);return o&&typeof o!="string"?o:i.create(e,i.Fragment,{children:o||void 0},void 0)}function hr(e,t,n){if(t.type==="element")return Wi(e,t,n);if(t.type==="mdxFlowExpression"||t.type==="mdxTextExpression")return Yi(e,t);if(t.type==="mdxJsxFlowElement"||t.type==="mdxJsxTextElement")return Qi(e,t,n);if(t.type==="mdxjsEsm")return Ki(e,t);if(t.type==="root")return Xi(e,t,n);if(t.type==="text")return Gi(e,t)}function Wi(e,t,n){const r=e.schema;let i=r;t.tagName.toLowerCase()==="svg"&&r.space==="html"&&(i=Un,e.schema=i),e.ancestors.push(t);const o=mr(e,t.tagName,!1),l=el(e,t);let a=Wn(e,t);return qi.has(t.tagName)&&(a=a.filter(function(u){return typeof u=="string"?!Si(u):!0})),pr(e,l,o,t),$n(l,a),e.ancestors.pop(),e.schema=r,e.create(t,o,l,n)}function Yi(e,t){if(t.data&&t.data.estree&&e.evaluater){const r=t.data.estree.body[0];return r.type,e.evaluater.evaluateExpression(r.expression)}We(e,t.position)}function Ki(e,t){if(t.data&&t.data.estree&&e.evaluater)return e.evaluater.evaluateProgram(t.data.estree);We(e,t.position)}function Qi(e,t,n){const r=e.schema;let i=r;t.name==="svg"&&r.space==="html"&&(i=Un,e.schema=i),e.ancestors.push(t);const o=t.name===null?e.Fragment:mr(e,t.name,!0),l=nl(e,t),a=Wn(e,t);return pr(e,l,o,t),$n(l,a),e.ancestors.pop(),e.schema=r,e.create(t,o,l,n)}function Xi(e,t,n){const r={};return $n(r,Wn(e,t)),e.create(t,e.Fragment,r,n)}function Gi(e,t){return t.value}function pr(e,t,n,r){typeof n!="string"&&n!==e.Fragment&&e.passNode&&(t.node=r)}function $n(e,t){if(t.length>0){const n=t.length>1?t:t[0];n&&(e.children=n)}}function Ji(e,t,n){return r;function r(i,o,l,a){const s=Array.isArray(l.children)?n:t;return a?s(o,l,a):s(o,l)}}function Zi(e,t){return n;function n(r,i,o,l){const a=Array.isArray(o.children),u=qn(r);return t(i,o,l,a,{columnNumber:u?u.column-1:void 0,fileName:e,lineNumber:u?u.line:void 0},void 0)}}function el(e,t){const n={};let r,i;for(i in t.properties)if(i!=="children"&&Vn.call(t.properties,i)){const o=tl(e,i,t.properties[i]);if(o){const[l,a]=o;e.tableCellAlignToStyle&&l==="align"&&typeof a=="string"&&Vi.has(t.tagName)?r=a:n[l]=a}}if(r){const o=n.style||(n.style={});o[e.stylePropertyNameCase==="css"?"text-align":"textAlign"]=r}return n}function nl(e,t){const n={};for(const r of t.attributes)if(r.type==="mdxJsxExpressionAttribute")if(r.data&&r.data.estree&&e.evaluater){const o=r.data.estree.body[0];o.type;const l=o.expression;l.type;const a=l.properties[0];a.type,Object.assign(n,e.evaluater.evaluateExpression(a.argument))}else We(e,t.position);else{const i=r.name;let o;if(r.value&&typeof r.value=="object")if(r.value.data&&r.value.data.estree&&e.evaluater){const a=r.value.data.estree.body[0];a.type,o=e.evaluater.evaluateExpression(a.expression)}else We(e,t.position);else o=r.value===null?!0:r.value;n[i]=o}return n}function Wn(e,t){const n=[];let r=-1;const i=e.passKeys?new Map:Hi;for(;++ri?0:i+t:t=t>i?i:t,n=n>0?n:0,r.length<1e4)l=Array.from(r),l.unshift(t,n),e.splice(...l);else for(n&&e.splice(t,n);o0?(ie(e,e.length,0,t),e):t}const Ct={}.hasOwnProperty;function gr(e){const t={};let n=-1;for(;++n13&&n<32||n>126&&n<160||n>55295&&n<57344||n>64975&&n<65008||(n&65535)===65535||(n&65535)===65534||n>1114111?"�":String.fromCodePoint(n)}function ce(e){return e.replace(/[\t\n\r ]+/g," ").replace(/^ | $/g,"").toLowerCase().toUpperCase()}const J=be(/[A-Za-z]/),X=be(/[\dA-Za-z]/),fl=be(/[#-'*+\--9=?A-Z^-~]/);function rn(e){return e!==null&&(e<32||e===127)}const Rn=be(/\d/),hl=be(/[\dA-Fa-f]/),pl=be(/[!-/:-@[-`{-~]/);function z(e){return e!==null&&e<-2}function W(e){return e!==null&&(e<0||e===32)}function O(e){return e===-2||e===-1||e===32}const un=be(new RegExp("\\p{P}|\\p{S}","u")),Te=be(/\s/);function be(e){return t;function t(n){return n!==null&&n>-1&&e.test(String.fromCharCode(n))}}function _e(e){const t=[];let n=-1,r=0,i=0;for(;++n55295&&o<57344){const a=e.charCodeAt(n+1);o<56320&&a>56319&&a<57344?(l=String.fromCharCode(o,a),i=1):l="�"}else l=String.fromCharCode(o);l&&(t.push(e.slice(r,n),encodeURIComponent(l)),r=n+i+1,l=""),i&&(n+=i,i=0)}return t.join("")+e.slice(r)}function B(e,t,n,r){const i=r?r-1:Number.POSITIVE_INFINITY;let o=0;return l;function l(u){return O(u)?(e.enter(n),a(u)):t(u)}function a(u){return O(u)&&o++l))return;const M=t.events.length;let H=M,j,k;for(;H--;)if(t.events[H][0]==="exit"&&t.events[H][1].type==="chunkFlow"){if(j){k=t.events[H][1].end;break}j=!0}for(y(r),b=M;bC;){const _=n[R];t.containerState=_[1],_[0].exit.call(t,e)}n.length=C}function I(){i.write([null]),o=void 0,i=void 0,t.containerState._closeFlow=void 0}}function kl(e,t,n){return B(e,e.attempt(this.parser.constructs.document,t,n),"linePrefix",this.parser.constructs.disable.null.includes("codeIndented")?void 0:4)}function Fe(e){if(e===null||W(e)||Te(e))return 1;if(un(e))return 2}function sn(e,t,n){const r=[];let i=-1;for(;++i1&&e[n][1].end.offset-e[n][1].start.offset>1?2:1;const c={...e[r][1].end},p={...e[n][1].start};It(c,-u),It(p,u),l={type:u>1?"strongSequence":"emphasisSequence",start:c,end:{...e[r][1].end}},a={type:u>1?"strongSequence":"emphasisSequence",start:{...e[n][1].start},end:p},o={type:u>1?"strongText":"emphasisText",start:{...e[r][1].end},end:{...e[n][1].start}},i={type:u>1?"strong":"emphasis",start:{...l.start},end:{...a.end}},e[r][1].end={...l.start},e[n][1].start={...a.end},s=[],e[r][1].end.offset-e[r][1].start.offset&&(s=le(s,[["enter",e[r][1],t],["exit",e[r][1],t]])),s=le(s,[["enter",i,t],["enter",l,t],["exit",l,t],["enter",o,t]]),s=le(s,sn(t.parser.constructs.insideSpan.null,e.slice(r+1,n),t)),s=le(s,[["exit",o,t],["enter",a,t],["exit",a,t],["exit",i,t]]),e[n][1].end.offset-e[n][1].start.offset?(f=2,s=le(s,[["enter",e[n][1],t],["exit",e[n][1],t]])):f=0,ie(e,r-1,n-r+3,s),n=r+s.length-f-2;break}}for(n=-1;++n0&&O(b)?B(e,I,"linePrefix",o+1)(b):I(b)}function I(b){return b===null||z(b)?e.check(Tt,w,R)(b):(e.enter("codeFlowValue"),C(b))}function C(b){return b===null||z(b)?(e.exit("codeFlowValue"),I(b)):(e.consume(b),C)}function R(b){return e.exit("codeFenced"),t(b)}function _(b,M,H){let j=0;return k;function k(A){return b.enter("lineEnding"),b.consume(A),b.exit("lineEnding"),v}function v(A){return b.enter("codeFencedFence"),O(A)?B(b,P,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(A):P(A)}function P(A){return A===a?(b.enter("codeFencedFenceSequence"),U(A)):H(A)}function U(A){return A===a?(j++,b.consume(A),U):j>=l?(b.exit("codeFencedFenceSequence"),O(A)?B(b,D,"whitespace")(A):D(A)):H(A)}function D(A){return A===null||z(A)?(b.exit("codeFencedFence"),M(A)):H(A)}}}function zl(e,t,n){const r=this;return i;function i(l){return l===null?n(l):(e.enter("lineEnding"),e.consume(l),e.exit("lineEnding"),o)}function o(l){return r.parser.lazy[r.now().line]?n(l):t(l)}}const gn={name:"codeIndented",tokenize:Ll},Dl={partial:!0,tokenize:Fl};function Ll(e,t,n){const r=this;return i;function i(s){return e.enter("codeIndented"),B(e,o,"linePrefix",5)(s)}function o(s){const f=r.events[r.events.length-1];return f&&f[1].type==="linePrefix"&&f[2].sliceSerialize(f[1],!0).length>=4?l(s):n(s)}function l(s){return s===null?u(s):z(s)?e.attempt(Dl,l,u)(s):(e.enter("codeFlowValue"),a(s))}function a(s){return s===null||z(s)?(e.exit("codeFlowValue"),l(s)):(e.consume(s),a)}function u(s){return e.exit("codeIndented"),t(s)}}function Fl(e,t,n){const r=this;return i;function i(l){return r.parser.lazy[r.now().line]?n(l):z(l)?(e.enter("lineEnding"),e.consume(l),e.exit("lineEnding"),i):B(e,o,"linePrefix",5)(l)}function o(l){const a=r.events[r.events.length-1];return a&&a[1].type==="linePrefix"&&a[2].sliceSerialize(a[1],!0).length>=4?t(l):z(l)?i(l):n(l)}}const Rl={name:"codeText",previous:Ol,resolve:_l,tokenize:Ml};function _l(e){let t=e.length-4,n=3,r,i;if((e[n][1].type==="lineEnding"||e[n][1].type==="space")&&(e[t][1].type==="lineEnding"||e[t][1].type==="space")){for(r=n;++r=this.left.length+this.right.length)throw new RangeError("Cannot access index `"+t+"` in a splice buffer of size `"+(this.left.length+this.right.length)+"`");return tthis.left.length?this.right.slice(this.right.length-r+this.left.length,this.right.length-t+this.left.length).reverse():this.left.slice(t).concat(this.right.slice(this.right.length-r+this.left.length).reverse())}splice(t,n,r){const i=n||0;this.setCursor(Math.trunc(t));const o=this.right.splice(this.right.length-i,Number.POSITIVE_INFINITY);return r&&He(this.left,r),o.reverse()}pop(){return this.setCursor(Number.POSITIVE_INFINITY),this.left.pop()}push(t){this.setCursor(Number.POSITIVE_INFINITY),this.left.push(t)}pushMany(t){this.setCursor(Number.POSITIVE_INFINITY),He(this.left,t)}unshift(t){this.setCursor(0),this.right.push(t)}unshiftMany(t){this.setCursor(0),He(this.right,t.reverse())}setCursor(t){if(!(t===this.left.length||t>this.left.length&&this.right.length===0||t<0&&this.left.length===0))if(t=4?t(l):e.interrupt(r.parser.constructs.flow,n,t)(l)}}function Sr(e,t,n,r,i,o,l,a,u){const s=u||Number.POSITIVE_INFINITY;let f=0;return c;function c(y){return y===60?(e.enter(r),e.enter(i),e.enter(o),e.consume(y),e.exit(o),p):y===null||y===32||y===41||rn(y)?n(y):(e.enter(r),e.enter(l),e.enter(a),e.enter("chunkString",{contentType:"string"}),w(y))}function p(y){return y===62?(e.enter(o),e.consume(y),e.exit(o),e.exit(i),e.exit(r),t):(e.enter(a),e.enter("chunkString",{contentType:"string"}),h(y))}function h(y){return y===62?(e.exit("chunkString"),e.exit(a),p(y)):y===null||y===60||z(y)?n(y):(e.consume(y),y===92?g:h)}function g(y){return y===60||y===62||y===92?(e.consume(y),h):h(y)}function w(y){return!f&&(y===null||y===41||W(y))?(e.exit("chunkString"),e.exit(a),e.exit(l),e.exit(r),t(y)):f999||h===null||h===91||h===93&&!u||h===94&&!a&&"_hiddenFootnoteSupport"in l.parser.constructs?n(h):h===93?(e.exit(o),e.enter(i),e.consume(h),e.exit(i),e.exit(r),t):z(h)?(e.enter("lineEnding"),e.consume(h),e.exit("lineEnding"),f):(e.enter("chunkString",{contentType:"string"}),c(h))}function c(h){return h===null||h===91||h===93||z(h)||a++>999?(e.exit("chunkString"),f(h)):(e.consume(h),u||(u=!O(h)),h===92?p:c)}function p(h){return h===91||h===92||h===93?(e.consume(h),a++,c):c(h)}}function Er(e,t,n,r,i,o){let l;return a;function a(p){return p===34||p===39||p===40?(e.enter(r),e.enter(i),e.consume(p),e.exit(i),l=p===40?41:p,u):n(p)}function u(p){return p===l?(e.enter(i),e.consume(p),e.exit(i),e.exit(r),t):(e.enter(o),s(p))}function s(p){return p===l?(e.exit(o),u(l)):p===null?n(p):z(p)?(e.enter("lineEnding"),e.consume(p),e.exit("lineEnding"),B(e,s,"linePrefix")):(e.enter("chunkString",{contentType:"string"}),f(p))}function f(p){return p===l||p===null||z(p)?(e.exit("chunkString"),s(p)):(e.consume(p),p===92?c:f)}function c(p){return p===l||p===92?(e.consume(p),f):f(p)}}function Ve(e,t){let n;return r;function r(i){return z(i)?(e.enter("lineEnding"),e.consume(i),e.exit("lineEnding"),n=!0,r):O(i)?B(e,r,n?"linePrefix":"lineSuffix")(i):t(i)}}const $l={name:"definition",tokenize:Yl},Wl={partial:!0,tokenize:Kl};function Yl(e,t,n){const r=this;let i;return o;function o(h){return e.enter("definition"),l(h)}function l(h){return Cr.call(r,e,a,n,"definitionLabel","definitionLabelMarker","definitionLabelString")(h)}function a(h){return i=ce(r.sliceSerialize(r.events[r.events.length-1][1]).slice(1,-1)),h===58?(e.enter("definitionMarker"),e.consume(h),e.exit("definitionMarker"),u):n(h)}function u(h){return W(h)?Ve(e,s)(h):s(h)}function s(h){return Sr(e,f,n,"definitionDestination","definitionDestinationLiteral","definitionDestinationLiteralMarker","definitionDestinationRaw","definitionDestinationString")(h)}function f(h){return e.attempt(Wl,c,c)(h)}function c(h){return O(h)?B(e,p,"whitespace")(h):p(h)}function p(h){return h===null||z(h)?(e.exit("definition"),r.parser.defined.push(i),t(h)):n(h)}}function Kl(e,t,n){return r;function r(a){return W(a)?Ve(e,i)(a):n(a)}function i(a){return Er(e,o,n,"definitionTitle","definitionTitleMarker","definitionTitleString")(a)}function o(a){return O(a)?B(e,l,"whitespace")(a):l(a)}function l(a){return a===null||z(a)?t(a):n(a)}}const Ql={name:"hardBreakEscape",tokenize:Xl};function Xl(e,t,n){return r;function r(o){return e.enter("hardBreakEscape"),e.consume(o),i}function i(o){return z(o)?(e.exit("hardBreakEscape"),t(o)):n(o)}}const Gl={name:"headingAtx",resolve:Jl,tokenize:Zl};function Jl(e,t){let n=e.length-2,r=3,i,o;return e[r][1].type==="whitespace"&&(r+=2),n-2>r&&e[n][1].type==="whitespace"&&(n-=2),e[n][1].type==="atxHeadingSequence"&&(r===n-1||n-4>r&&e[n-2][1].type==="whitespace")&&(n-=r+1===n?2:4),n>r&&(i={type:"atxHeadingText",start:e[r][1].start,end:e[n][1].end},o={type:"chunkText",start:e[r][1].start,end:e[n][1].end,contentType:"text"},ie(e,r,n-r+1,[["enter",i,t],["enter",o,t],["exit",o,t],["exit",i,t]])),e}function Zl(e,t,n){let r=0;return i;function i(f){return e.enter("atxHeading"),o(f)}function o(f){return e.enter("atxHeadingSequence"),l(f)}function l(f){return f===35&&r++<6?(e.consume(f),l):f===null||W(f)?(e.exit("atxHeadingSequence"),a(f)):n(f)}function a(f){return f===35?(e.enter("atxHeadingSequence"),u(f)):f===null||z(f)?(e.exit("atxHeading"),t(f)):O(f)?B(e,a,"whitespace")(f):(e.enter("atxHeadingText"),s(f))}function u(f){return f===35?(e.consume(f),u):(e.exit("atxHeadingSequence"),a(f))}function s(f){return f===null||f===35||W(f)?(e.exit("atxHeadingText"),a(f)):(e.consume(f),s)}}const eo=["address","article","aside","base","basefont","blockquote","body","caption","center","col","colgroup","dd","details","dialog","dir","div","dl","dt","fieldset","figcaption","figure","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hr","html","iframe","legend","li","link","main","menu","menuitem","nav","noframes","ol","optgroup","option","p","param","search","section","summary","table","tbody","td","tfoot","th","thead","title","tr","track","ul"],vt=["pre","script","style","textarea"],no={concrete:!0,name:"htmlFlow",resolveTo:io,tokenize:lo},to={partial:!0,tokenize:ao},ro={partial:!0,tokenize:oo};function io(e){let t=e.length;for(;t--&&!(e[t][0]==="enter"&&e[t][1].type==="htmlFlow"););return t>1&&e[t-2][1].type==="linePrefix"&&(e[t][1].start=e[t-2][1].start,e[t+1][1].start=e[t-2][1].start,e.splice(t-2,2)),e}function lo(e,t,n){const r=this;let i,o,l,a,u;return s;function s(d){return f(d)}function f(d){return e.enter("htmlFlow"),e.enter("htmlFlowData"),e.consume(d),c}function c(d){return d===33?(e.consume(d),p):d===47?(e.consume(d),o=!0,w):d===63?(e.consume(d),i=3,r.interrupt?t:m):J(d)?(e.consume(d),l=String.fromCharCode(d),S):n(d)}function p(d){return d===45?(e.consume(d),i=2,h):d===91?(e.consume(d),i=5,a=0,g):J(d)?(e.consume(d),i=4,r.interrupt?t:m):n(d)}function h(d){return d===45?(e.consume(d),r.interrupt?t:m):n(d)}function g(d){const ue="CDATA[";return d===ue.charCodeAt(a++)?(e.consume(d),a===ue.length?r.interrupt?t:P:g):n(d)}function w(d){return J(d)?(e.consume(d),l=String.fromCharCode(d),S):n(d)}function S(d){if(d===null||d===47||d===62||W(d)){const ue=d===47,we=l.toLowerCase();return!ue&&!o&&vt.includes(we)?(i=1,r.interrupt?t(d):P(d)):eo.includes(l.toLowerCase())?(i=6,ue?(e.consume(d),y):r.interrupt?t(d):P(d)):(i=7,r.interrupt&&!r.parser.lazy[r.now().line]?n(d):o?I(d):C(d))}return d===45||X(d)?(e.consume(d),l+=String.fromCharCode(d),S):n(d)}function y(d){return d===62?(e.consume(d),r.interrupt?t:P):n(d)}function I(d){return O(d)?(e.consume(d),I):k(d)}function C(d){return d===47?(e.consume(d),k):d===58||d===95||J(d)?(e.consume(d),R):O(d)?(e.consume(d),C):k(d)}function R(d){return d===45||d===46||d===58||d===95||X(d)?(e.consume(d),R):_(d)}function _(d){return d===61?(e.consume(d),b):O(d)?(e.consume(d),_):C(d)}function b(d){return d===null||d===60||d===61||d===62||d===96?n(d):d===34||d===39?(e.consume(d),u=d,M):O(d)?(e.consume(d),b):H(d)}function M(d){return d===u?(e.consume(d),u=null,j):d===null||z(d)?n(d):(e.consume(d),M)}function H(d){return d===null||d===34||d===39||d===47||d===60||d===61||d===62||d===96||W(d)?_(d):(e.consume(d),H)}function j(d){return d===47||d===62||O(d)?C(d):n(d)}function k(d){return d===62?(e.consume(d),v):n(d)}function v(d){return d===null||z(d)?P(d):O(d)?(e.consume(d),v):n(d)}function P(d){return d===45&&i===2?(e.consume(d),q):d===60&&i===1?(e.consume(d),K):d===62&&i===4?(e.consume(d),ae):d===63&&i===3?(e.consume(d),m):d===93&&i===5?(e.consume(d),pe):z(d)&&(i===6||i===7)?(e.exit("htmlFlowData"),e.check(to,me,U)(d)):d===null||z(d)?(e.exit("htmlFlowData"),U(d)):(e.consume(d),P)}function U(d){return e.check(ro,D,me)(d)}function D(d){return e.enter("lineEnding"),e.consume(d),e.exit("lineEnding"),A}function A(d){return d===null||z(d)?U(d):(e.enter("htmlFlowData"),P(d))}function q(d){return d===45?(e.consume(d),m):P(d)}function K(d){return d===47?(e.consume(d),l="",oe):P(d)}function oe(d){if(d===62){const ue=l.toLowerCase();return vt.includes(ue)?(e.consume(d),ae):P(d)}return J(d)&&l.length<8?(e.consume(d),l+=String.fromCharCode(d),oe):P(d)}function pe(d){return d===93?(e.consume(d),m):P(d)}function m(d){return d===62?(e.consume(d),ae):d===45&&i===2?(e.consume(d),m):P(d)}function ae(d){return d===null||z(d)?(e.exit("htmlFlowData"),me(d)):(e.consume(d),ae)}function me(d){return e.exit("htmlFlow"),t(d)}}function oo(e,t,n){const r=this;return i;function i(l){return z(l)?(e.enter("lineEnding"),e.consume(l),e.exit("lineEnding"),o):n(l)}function o(l){return r.parser.lazy[r.now().line]?n(l):t(l)}}function ao(e,t,n){return r;function r(i){return e.enter("lineEnding"),e.consume(i),e.exit("lineEnding"),e.attempt(Qe,t,n)}}const uo={name:"htmlText",tokenize:so};function so(e,t,n){const r=this;let i,o,l;return a;function a(m){return e.enter("htmlText"),e.enter("htmlTextData"),e.consume(m),u}function u(m){return m===33?(e.consume(m),s):m===47?(e.consume(m),_):m===63?(e.consume(m),C):J(m)?(e.consume(m),H):n(m)}function s(m){return m===45?(e.consume(m),f):m===91?(e.consume(m),o=0,g):J(m)?(e.consume(m),I):n(m)}function f(m){return m===45?(e.consume(m),h):n(m)}function c(m){return m===null?n(m):m===45?(e.consume(m),p):z(m)?(l=c,K(m)):(e.consume(m),c)}function p(m){return m===45?(e.consume(m),h):c(m)}function h(m){return m===62?q(m):m===45?p(m):c(m)}function g(m){const ae="CDATA[";return m===ae.charCodeAt(o++)?(e.consume(m),o===ae.length?w:g):n(m)}function w(m){return m===null?n(m):m===93?(e.consume(m),S):z(m)?(l=w,K(m)):(e.consume(m),w)}function S(m){return m===93?(e.consume(m),y):w(m)}function y(m){return m===62?q(m):m===93?(e.consume(m),y):w(m)}function I(m){return m===null||m===62?q(m):z(m)?(l=I,K(m)):(e.consume(m),I)}function C(m){return m===null?n(m):m===63?(e.consume(m),R):z(m)?(l=C,K(m)):(e.consume(m),C)}function R(m){return m===62?q(m):C(m)}function _(m){return J(m)?(e.consume(m),b):n(m)}function b(m){return m===45||X(m)?(e.consume(m),b):M(m)}function M(m){return z(m)?(l=M,K(m)):O(m)?(e.consume(m),M):q(m)}function H(m){return m===45||X(m)?(e.consume(m),H):m===47||m===62||W(m)?j(m):n(m)}function j(m){return m===47?(e.consume(m),q):m===58||m===95||J(m)?(e.consume(m),k):z(m)?(l=j,K(m)):O(m)?(e.consume(m),j):q(m)}function k(m){return m===45||m===46||m===58||m===95||X(m)?(e.consume(m),k):v(m)}function v(m){return m===61?(e.consume(m),P):z(m)?(l=v,K(m)):O(m)?(e.consume(m),v):j(m)}function P(m){return m===null||m===60||m===61||m===62||m===96?n(m):m===34||m===39?(e.consume(m),i=m,U):z(m)?(l=P,K(m)):O(m)?(e.consume(m),P):(e.consume(m),D)}function U(m){return m===i?(e.consume(m),i=void 0,A):m===null?n(m):z(m)?(l=U,K(m)):(e.consume(m),U)}function D(m){return m===null||m===34||m===39||m===60||m===61||m===96?n(m):m===47||m===62||W(m)?j(m):(e.consume(m),D)}function A(m){return m===47||m===62||W(m)?j(m):n(m)}function q(m){return m===62?(e.consume(m),e.exit("htmlTextData"),e.exit("htmlText"),t):n(m)}function K(m){return e.exit("htmlTextData"),e.enter("lineEnding"),e.consume(m),e.exit("lineEnding"),oe}function oe(m){return O(m)?B(e,pe,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(m):pe(m)}function pe(m){return e.enter("htmlTextData"),l(m)}}const Qn={name:"labelEnd",resolveAll:po,resolveTo:mo,tokenize:go},co={tokenize:yo},fo={tokenize:ko},ho={tokenize:xo};function po(e){let t=-1;const n=[];for(;++t=3&&(s===null||z(s))?(e.exit("thematicBreak"),t(s)):n(s)}function u(s){return s===i?(e.consume(s),r++,u):(e.exit("thematicBreakSequence"),O(s)?B(e,a,"whitespace")(s):a(s))}}const Z={continuation:{tokenize:Po},exit:Do,name:"list",tokenize:vo},To={partial:!0,tokenize:Lo},Ao={partial:!0,tokenize:zo};function vo(e,t,n){const r=this,i=r.events[r.events.length-1];let o=i&&i[1].type==="linePrefix"?i[2].sliceSerialize(i[1],!0).length:0,l=0;return a;function a(h){const g=r.containerState.type||(h===42||h===43||h===45?"listUnordered":"listOrdered");if(g==="listUnordered"?!r.containerState.marker||h===r.containerState.marker:Rn(h)){if(r.containerState.type||(r.containerState.type=g,e.enter(g,{_container:!0})),g==="listUnordered")return e.enter("listItemPrefix"),h===42||h===45?e.check(tn,n,s)(h):s(h);if(!r.interrupt||h===49)return e.enter("listItemPrefix"),e.enter("listItemValue"),u(h)}return n(h)}function u(h){return Rn(h)&&++l<10?(e.consume(h),u):(!r.interrupt||l<2)&&(r.containerState.marker?h===r.containerState.marker:h===41||h===46)?(e.exit("listItemValue"),s(h)):n(h)}function s(h){return e.enter("listItemMarker"),e.consume(h),e.exit("listItemMarker"),r.containerState.marker=r.containerState.marker||h,e.check(Qe,r.interrupt?n:f,e.attempt(To,p,c))}function f(h){return r.containerState.initialBlankLine=!0,o++,p(h)}function c(h){return O(h)?(e.enter("listItemPrefixWhitespace"),e.consume(h),e.exit("listItemPrefixWhitespace"),p):n(h)}function p(h){return r.containerState.size=o+r.sliceSerialize(e.exit("listItemPrefix"),!0).length,t(h)}}function Po(e,t,n){const r=this;return r.containerState._closeFlow=void 0,e.check(Qe,i,o);function i(a){return r.containerState.furtherBlankLines=r.containerState.furtherBlankLines||r.containerState.initialBlankLine,B(e,t,"listItemIndent",r.containerState.size+1)(a)}function o(a){return r.containerState.furtherBlankLines||!O(a)?(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,l(a)):(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,e.attempt(Ao,t,l)(a))}function l(a){return r.containerState._closeFlow=!0,r.interrupt=void 0,B(e,e.attempt(Z,t,n),"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(a)}}function zo(e,t,n){const r=this;return B(e,i,"listItemIndent",r.containerState.size+1);function i(o){const l=r.events[r.events.length-1];return l&&l[1].type==="listItemIndent"&&l[2].sliceSerialize(l[1],!0).length===r.containerState.size?t(o):n(o)}}function Do(e){e.exit(this.containerState.type)}function Lo(e,t,n){const r=this;return B(e,i,"listItemPrefixWhitespace",r.parser.constructs.disable.null.includes("codeIndented")?void 0:5);function i(o){const l=r.events[r.events.length-1];return!O(o)&&l&&l[1].type==="listItemPrefixWhitespace"?t(o):n(o)}}const Pt={name:"setextUnderline",resolveTo:Fo,tokenize:Ro};function Fo(e,t){let n=e.length,r,i,o;for(;n--;)if(e[n][0]==="enter"){if(e[n][1].type==="content"){r=n;break}e[n][1].type==="paragraph"&&(i=n)}else e[n][1].type==="content"&&e.splice(n,1),!o&&e[n][1].type==="definition"&&(o=n);const l={type:"setextHeading",start:{...e[r][1].start},end:{...e[e.length-1][1].end}};return e[i][1].type="setextHeadingText",o?(e.splice(i,0,["enter",l,t]),e.splice(o+1,0,["exit",e[r][1],t]),e[r][1].end={...e[o][1].end}):e[r][1]=l,e.push(["exit",l,t]),e}function Ro(e,t,n){const r=this;let i;return o;function o(s){let f=r.events.length,c;for(;f--;)if(r.events[f][1].type!=="lineEnding"&&r.events[f][1].type!=="linePrefix"&&r.events[f][1].type!=="content"){c=r.events[f][1].type==="paragraph";break}return!r.parser.lazy[r.now().line]&&(r.interrupt||c)?(e.enter("setextHeadingLine"),i=s,l(s)):n(s)}function l(s){return e.enter("setextHeadingLineSequence"),a(s)}function a(s){return s===i?(e.consume(s),a):(e.exit("setextHeadingLineSequence"),O(s)?B(e,u,"lineSuffix")(s):u(s))}function u(s){return s===null||z(s)?(e.exit("setextHeadingLine"),t(s)):n(s)}}const _o={tokenize:Oo};function Oo(e){const t=this,n=e.attempt(Qe,r,e.attempt(this.parser.constructs.flowInitial,i,B(e,e.attempt(this.parser.constructs.flow,i,e.attempt(jl,i)),"linePrefix")));return n;function r(o){if(o===null){e.consume(o);return}return e.enter("lineEndingBlank"),e.consume(o),e.exit("lineEndingBlank"),t.currentConstruct=void 0,n}function i(o){if(o===null){e.consume(o);return}return e.enter("lineEnding"),e.consume(o),e.exit("lineEnding"),t.currentConstruct=void 0,n}}const Mo={resolveAll:Tr()},No=Ir("string"),Bo=Ir("text");function Ir(e){return{resolveAll:Tr(e==="text"?jo:void 0),tokenize:t};function t(n){const r=this,i=this.parser.constructs[e],o=n.attempt(i,l,a);return l;function l(f){return s(f)?o(f):a(f)}function a(f){if(f===null){n.consume(f);return}return n.enter("data"),n.consume(f),u}function u(f){return s(f)?(n.exit("data"),o(f)):(n.consume(f),u)}function s(f){if(f===null)return!0;const c=i[f];let p=-1;if(c)for(;++p-1){const a=l[0];typeof a=="string"?l[0]=a.slice(r):l.shift()}o>0&&l.push(e[i].slice(0,o))}return l}function Zo(e,t){let n=-1;const r=[];let i;for(;++n4&&n.slice(0,4)==="data"&&vi.test(t)){if(t.charAt(4)==="-"){const o=t.slice(5).replace(mt,Di);r="data"+o.charAt(0).toUpperCase()+o.slice(1)}else{const o=t.slice(4);if(!mt.test(o)){let l=o.replace(Ai,zi);l.charAt(0)!=="-"&&(l="-"+l),t="data"+l}}i=Hn}return new i(r,t)}function zi(e){return"-"+e.toLowerCase()}function Di(e){return e.charAt(1).toUpperCase()}const Li=tr([rr,Ei,or,ar,ur],"html"),Un=tr([rr,Ii,or,ar,ur],"svg");function Ri(e){return e.join(" ").trim()}var De={},mn,dt;function Fi(){if(dt)return mn;dt=1;var e=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//g,t=/\n/g,n=/^\s*/,r=/^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/,i=/^:\s*/,o=/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/,l=/^[;\s]*/,a=/^\s+|\s+$/g,u=` +`,s="/",f="*",c="",p="comment",h="declaration";function g(S,y){if(typeof S!="string")throw new TypeError("First argument must be a string");if(!S)return[];y=y||{};var I=1,C=1;function F(D){var A=D.match(t);A&&(I+=A.length);var q=D.lastIndexOf(u);C=~q?D.length-q:C+D.length}function _(){var D={line:I,column:C};return function(A){return A.position=new b(D),j(),A}}function b(D){this.start=D,this.end={line:I,column:C},this.source=y.source}b.prototype.content=S;function M(D){var A=new Error(y.source+":"+I+":"+C+": "+D);if(A.reason=D,A.filename=y.source,A.line=I,A.column=C,A.source=S,!y.silent)throw A}function H(D){var A=D.exec(S);if(A){var q=A[0];return F(q),S=S.slice(q.length),A}}function j(){H(n)}function k(D){var A;for(D=D||[];A=v();)A!==!1&&D.push(A);return D}function v(){var D=_();if(!(s!=S.charAt(0)||f!=S.charAt(1))){for(var A=2;c!=S.charAt(A)&&(f!=S.charAt(A)||s!=S.charAt(A+1));)++A;if(A+=2,c===S.charAt(A-1))return M("End of comment missing");var q=S.slice(2,A-2);return C+=2,F(q),S=S.slice(A),C+=2,D({type:p,comment:q})}}function P(){var D=_(),A=H(r);if(A){if(v(),!H(i))return M("property missing ':'");var q=H(o),K=D({type:h,property:w(A[0].replace(e,c)),value:q?w(q[0].replace(e,c)):c});return H(l),K}}function U(){var D=[];k(D);for(var A;A=P();)A!==!1&&(D.push(A),k(D));return D}return j(),U()}function w(S){return S?S.replace(a,c):c}return mn=g,mn}var gt;function _i(){if(gt)return De;gt=1;var e=De&&De.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(De,"__esModule",{value:!0}),De.default=n;const t=e(Fi());function n(r,i){let o=null;if(!r||typeof r!="string")return o;const l=(0,t.default)(r),a=typeof i=="function";return l.forEach(u=>{if(u.type!=="declaration")return;const{property:s,value:f}=u;a?i(s,f,u):f&&(o=o||{},o[s]=f)}),o}return De}var Be={},yt;function Oi(){if(yt)return Be;yt=1,Object.defineProperty(Be,"__esModule",{value:!0}),Be.camelCase=void 0;var e=/^--[a-zA-Z0-9_-]+$/,t=/-([a-z])/g,n=/^[^-]+$/,r=/^-(webkit|moz|ms|o|khtml)-/,i=/^-(ms)-/,o=function(s){return!s||n.test(s)||e.test(s)},l=function(s,f){return f.toUpperCase()},a=function(s,f){return"".concat(f,"-")},u=function(s,f){return f===void 0&&(f={}),o(s)?s:(s=s.toLowerCase(),f.reactCompat?s=s.replace(i,a):s=s.replace(r,a),s.replace(t,l))};return Be.camelCase=u,Be}var je,kt;function Mi(){if(kt)return je;kt=1;var e=je&&je.__importDefault||function(i){return i&&i.__esModule?i:{default:i}},t=e(_i()),n=Oi();function r(i,o){var l={};return!i||typeof i!="string"||(0,t.default)(i,function(a,u){a&&u&&(l[(0,n.camelCase)(a,o)]=u)}),l}return r.default=r,je=r,je}var Ni=Mi();const Bi=nr(Ni),sr=cr("end"),qn=cr("start");function cr(e){return t;function t(n){const r=n&&n.position&&n.position[e]||{};if(typeof r.line=="number"&&r.line>0&&typeof r.column=="number"&&r.column>0)return{line:r.line,column:r.column,offset:typeof r.offset=="number"&&r.offset>-1?r.offset:void 0}}}function ji(e){const t=qn(e),n=sr(e);if(t&&n)return{start:t,end:n}}function qe(e){return!e||typeof e!="object"?"":"position"in e||"type"in e?xt(e.position):"start"in e||"end"in e?xt(e):"line"in e||"column"in e?Rn(e):""}function Rn(e){return bt(e&&e.line)+":"+bt(e&&e.column)}function xt(e){return Rn(e&&e.start)+"-"+Rn(e&&e.end)}function bt(e){return e&&typeof e=="number"?e:1}class G extends Error{constructor(t,n,r){super(),typeof n=="string"&&(r=n,n=void 0);let i="",o={},l=!1;if(n&&("line"in n&&"column"in n?o={place:n}:"start"in n&&"end"in n?o={place:n}:"type"in n?o={ancestors:[n],place:n.position}:o={...n}),typeof t=="string"?i=t:!o.cause&&t&&(l=!0,i=t.message,o.cause=t),!o.ruleId&&!o.source&&typeof r=="string"){const u=r.indexOf(":");u===-1?o.ruleId=r:(o.source=r.slice(0,u),o.ruleId=r.slice(u+1))}if(!o.place&&o.ancestors&&o.ancestors){const u=o.ancestors[o.ancestors.length-1];u&&(o.place=u.position)}const a=o.place&&"start"in o.place?o.place.start:o.place;this.ancestors=o.ancestors||void 0,this.cause=o.cause||void 0,this.column=a?a.column:void 0,this.fatal=void 0,this.file="",this.message=i,this.line=a?a.line:void 0,this.name=qe(o.place)||"1:1",this.place=o.place||void 0,this.reason=this.message,this.ruleId=o.ruleId||void 0,this.source=o.source||void 0,this.stack=l&&o.cause&&typeof o.cause.stack=="string"?o.cause.stack:"",this.actual=void 0,this.expected=void 0,this.note=void 0,this.url=void 0}}G.prototype.file="";G.prototype.name="";G.prototype.reason="";G.prototype.message="";G.prototype.stack="";G.prototype.column=void 0;G.prototype.line=void 0;G.prototype.ancestors=void 0;G.prototype.cause=void 0;G.prototype.fatal=void 0;G.prototype.place=void 0;G.prototype.ruleId=void 0;G.prototype.source=void 0;const Vn={}.hasOwnProperty,Hi=new Map,Ui=/[A-Z]/g,qi=new Set(["table","tbody","thead","tfoot","tr"]),Vi=new Set(["td","th"]),fr="https://github.com/syntax-tree/hast-util-to-jsx-runtime";function $i(e,t){if(!t||t.Fragment===void 0)throw new TypeError("Expected `Fragment` in options");const n=t.filePath||void 0;let r;if(t.development){if(typeof t.jsxDEV!="function")throw new TypeError("Expected `jsxDEV` in options when `development: true`");r=Zi(n,t.jsxDEV)}else{if(typeof t.jsx!="function")throw new TypeError("Expected `jsx` in production options");if(typeof t.jsxs!="function")throw new TypeError("Expected `jsxs` in production options");r=Ji(n,t.jsx,t.jsxs)}const i={Fragment:t.Fragment,ancestors:[],components:t.components||{},create:r,elementAttributeNameCase:t.elementAttributeNameCase||"react",evaluater:t.createEvaluater?t.createEvaluater():void 0,filePath:n,ignoreInvalidStyle:t.ignoreInvalidStyle||!1,passKeys:t.passKeys!==!1,passNode:t.passNode||!1,schema:t.space==="svg"?Un:Li,stylePropertyNameCase:t.stylePropertyNameCase||"dom",tableCellAlignToStyle:t.tableCellAlignToStyle!==!1},o=hr(i,e,void 0);return o&&typeof o!="string"?o:i.create(e,i.Fragment,{children:o||void 0},void 0)}function hr(e,t,n){if(t.type==="element")return Wi(e,t,n);if(t.type==="mdxFlowExpression"||t.type==="mdxTextExpression")return Yi(e,t);if(t.type==="mdxJsxFlowElement"||t.type==="mdxJsxTextElement")return Qi(e,t,n);if(t.type==="mdxjsEsm")return Ki(e,t);if(t.type==="root")return Xi(e,t,n);if(t.type==="text")return Gi(e,t)}function Wi(e,t,n){const r=e.schema;let i=r;t.tagName.toLowerCase()==="svg"&&r.space==="html"&&(i=Un,e.schema=i),e.ancestors.push(t);const o=mr(e,t.tagName,!1),l=el(e,t);let a=Wn(e,t);return qi.has(t.tagName)&&(a=a.filter(function(u){return typeof u=="string"?!Si(u):!0})),pr(e,l,o,t),$n(l,a),e.ancestors.pop(),e.schema=r,e.create(t,o,l,n)}function Yi(e,t){if(t.data&&t.data.estree&&e.evaluater){const r=t.data.estree.body[0];return r.type,e.evaluater.evaluateExpression(r.expression)}We(e,t.position)}function Ki(e,t){if(t.data&&t.data.estree&&e.evaluater)return e.evaluater.evaluateProgram(t.data.estree);We(e,t.position)}function Qi(e,t,n){const r=e.schema;let i=r;t.name==="svg"&&r.space==="html"&&(i=Un,e.schema=i),e.ancestors.push(t);const o=t.name===null?e.Fragment:mr(e,t.name,!0),l=nl(e,t),a=Wn(e,t);return pr(e,l,o,t),$n(l,a),e.ancestors.pop(),e.schema=r,e.create(t,o,l,n)}function Xi(e,t,n){const r={};return $n(r,Wn(e,t)),e.create(t,e.Fragment,r,n)}function Gi(e,t){return t.value}function pr(e,t,n,r){typeof n!="string"&&n!==e.Fragment&&e.passNode&&(t.node=r)}function $n(e,t){if(t.length>0){const n=t.length>1?t:t[0];n&&(e.children=n)}}function Ji(e,t,n){return r;function r(i,o,l,a){const s=Array.isArray(l.children)?n:t;return a?s(o,l,a):s(o,l)}}function Zi(e,t){return n;function n(r,i,o,l){const a=Array.isArray(o.children),u=qn(r);return t(i,o,l,a,{columnNumber:u?u.column-1:void 0,fileName:e,lineNumber:u?u.line:void 0},void 0)}}function el(e,t){const n={};let r,i;for(i in t.properties)if(i!=="children"&&Vn.call(t.properties,i)){const o=tl(e,i,t.properties[i]);if(o){const[l,a]=o;e.tableCellAlignToStyle&&l==="align"&&typeof a=="string"&&Vi.has(t.tagName)?r=a:n[l]=a}}if(r){const o=n.style||(n.style={});o[e.stylePropertyNameCase==="css"?"text-align":"textAlign"]=r}return n}function nl(e,t){const n={};for(const r of t.attributes)if(r.type==="mdxJsxExpressionAttribute")if(r.data&&r.data.estree&&e.evaluater){const o=r.data.estree.body[0];o.type;const l=o.expression;l.type;const a=l.properties[0];a.type,Object.assign(n,e.evaluater.evaluateExpression(a.argument))}else We(e,t.position);else{const i=r.name;let o;if(r.value&&typeof r.value=="object")if(r.value.data&&r.value.data.estree&&e.evaluater){const a=r.value.data.estree.body[0];a.type,o=e.evaluater.evaluateExpression(a.expression)}else We(e,t.position);else o=r.value===null?!0:r.value;n[i]=o}return n}function Wn(e,t){const n=[];let r=-1;const i=e.passKeys?new Map:Hi;for(;++ri?0:i+t:t=t>i?i:t,n=n>0?n:0,r.length<1e4)l=Array.from(r),l.unshift(t,n),e.splice(...l);else for(n&&e.splice(t,n);o0?(ie(e,e.length,0,t),e):t}const Ct={}.hasOwnProperty;function gr(e){const t={};let n=-1;for(;++n13&&n<32||n>126&&n<160||n>55295&&n<57344||n>64975&&n<65008||(n&65535)===65535||(n&65535)===65534||n>1114111?"�":String.fromCodePoint(n)}function ce(e){return e.replace(/[\t\n\r ]+/g," ").replace(/^ | $/g,"").toLowerCase().toUpperCase()}const J=be(/[A-Za-z]/),X=be(/[\dA-Za-z]/),fl=be(/[#-'*+\--9=?A-Z^-~]/);function rn(e){return e!==null&&(e<32||e===127)}const Fn=be(/\d/),hl=be(/[\dA-Fa-f]/),pl=be(/[!-/:-@[-`{-~]/);function z(e){return e!==null&&e<-2}function W(e){return e!==null&&(e<0||e===32)}function O(e){return e===-2||e===-1||e===32}const un=be(new RegExp("\\p{P}|\\p{S}","u")),Te=be(/\s/);function be(e){return t;function t(n){return n!==null&&n>-1&&e.test(String.fromCharCode(n))}}function _e(e){const t=[];let n=-1,r=0,i=0;for(;++n55295&&o<57344){const a=e.charCodeAt(n+1);o<56320&&a>56319&&a<57344?(l=String.fromCharCode(o,a),i=1):l="�"}else l=String.fromCharCode(o);l&&(t.push(e.slice(r,n),encodeURIComponent(l)),r=n+i+1,l=""),i&&(n+=i,i=0)}return t.join("")+e.slice(r)}function B(e,t,n,r){const i=r?r-1:Number.POSITIVE_INFINITY;let o=0;return l;function l(u){return O(u)?(e.enter(n),a(u)):t(u)}function a(u){return O(u)&&o++l))return;const M=t.events.length;let H=M,j,k;for(;H--;)if(t.events[H][0]==="exit"&&t.events[H][1].type==="chunkFlow"){if(j){k=t.events[H][1].end;break}j=!0}for(y(r),b=M;bC;){const _=n[F];t.containerState=_[1],_[0].exit.call(t,e)}n.length=C}function I(){i.write([null]),o=void 0,i=void 0,t.containerState._closeFlow=void 0}}function kl(e,t,n){return B(e,e.attempt(this.parser.constructs.document,t,n),"linePrefix",this.parser.constructs.disable.null.includes("codeIndented")?void 0:4)}function Re(e){if(e===null||W(e)||Te(e))return 1;if(un(e))return 2}function sn(e,t,n){const r=[];let i=-1;for(;++i1&&e[n][1].end.offset-e[n][1].start.offset>1?2:1;const c={...e[r][1].end},p={...e[n][1].start};It(c,-u),It(p,u),l={type:u>1?"strongSequence":"emphasisSequence",start:c,end:{...e[r][1].end}},a={type:u>1?"strongSequence":"emphasisSequence",start:{...e[n][1].start},end:p},o={type:u>1?"strongText":"emphasisText",start:{...e[r][1].end},end:{...e[n][1].start}},i={type:u>1?"strong":"emphasis",start:{...l.start},end:{...a.end}},e[r][1].end={...l.start},e[n][1].start={...a.end},s=[],e[r][1].end.offset-e[r][1].start.offset&&(s=le(s,[["enter",e[r][1],t],["exit",e[r][1],t]])),s=le(s,[["enter",i,t],["enter",l,t],["exit",l,t],["enter",o,t]]),s=le(s,sn(t.parser.constructs.insideSpan.null,e.slice(r+1,n),t)),s=le(s,[["exit",o,t],["enter",a,t],["exit",a,t],["exit",i,t]]),e[n][1].end.offset-e[n][1].start.offset?(f=2,s=le(s,[["enter",e[n][1],t],["exit",e[n][1],t]])):f=0,ie(e,r-1,n-r+3,s),n=r+s.length-f-2;break}}for(n=-1;++n0&&O(b)?B(e,I,"linePrefix",o+1)(b):I(b)}function I(b){return b===null||z(b)?e.check(Tt,w,F)(b):(e.enter("codeFlowValue"),C(b))}function C(b){return b===null||z(b)?(e.exit("codeFlowValue"),I(b)):(e.consume(b),C)}function F(b){return e.exit("codeFenced"),t(b)}function _(b,M,H){let j=0;return k;function k(A){return b.enter("lineEnding"),b.consume(A),b.exit("lineEnding"),v}function v(A){return b.enter("codeFencedFence"),O(A)?B(b,P,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(A):P(A)}function P(A){return A===a?(b.enter("codeFencedFenceSequence"),U(A)):H(A)}function U(A){return A===a?(j++,b.consume(A),U):j>=l?(b.exit("codeFencedFenceSequence"),O(A)?B(b,D,"whitespace")(A):D(A)):H(A)}function D(A){return A===null||z(A)?(b.exit("codeFencedFence"),M(A)):H(A)}}}function zl(e,t,n){const r=this;return i;function i(l){return l===null?n(l):(e.enter("lineEnding"),e.consume(l),e.exit("lineEnding"),o)}function o(l){return r.parser.lazy[r.now().line]?n(l):t(l)}}const gn={name:"codeIndented",tokenize:Ll},Dl={partial:!0,tokenize:Rl};function Ll(e,t,n){const r=this;return i;function i(s){return e.enter("codeIndented"),B(e,o,"linePrefix",5)(s)}function o(s){const f=r.events[r.events.length-1];return f&&f[1].type==="linePrefix"&&f[2].sliceSerialize(f[1],!0).length>=4?l(s):n(s)}function l(s){return s===null?u(s):z(s)?e.attempt(Dl,l,u)(s):(e.enter("codeFlowValue"),a(s))}function a(s){return s===null||z(s)?(e.exit("codeFlowValue"),l(s)):(e.consume(s),a)}function u(s){return e.exit("codeIndented"),t(s)}}function Rl(e,t,n){const r=this;return i;function i(l){return r.parser.lazy[r.now().line]?n(l):z(l)?(e.enter("lineEnding"),e.consume(l),e.exit("lineEnding"),i):B(e,o,"linePrefix",5)(l)}function o(l){const a=r.events[r.events.length-1];return a&&a[1].type==="linePrefix"&&a[2].sliceSerialize(a[1],!0).length>=4?t(l):z(l)?i(l):n(l)}}const Fl={name:"codeText",previous:Ol,resolve:_l,tokenize:Ml};function _l(e){let t=e.length-4,n=3,r,i;if((e[n][1].type==="lineEnding"||e[n][1].type==="space")&&(e[t][1].type==="lineEnding"||e[t][1].type==="space")){for(r=n;++r=this.left.length+this.right.length)throw new RangeError("Cannot access index `"+t+"` in a splice buffer of size `"+(this.left.length+this.right.length)+"`");return tthis.left.length?this.right.slice(this.right.length-r+this.left.length,this.right.length-t+this.left.length).reverse():this.left.slice(t).concat(this.right.slice(this.right.length-r+this.left.length).reverse())}splice(t,n,r){const i=n||0;this.setCursor(Math.trunc(t));const o=this.right.splice(this.right.length-i,Number.POSITIVE_INFINITY);return r&&He(this.left,r),o.reverse()}pop(){return this.setCursor(Number.POSITIVE_INFINITY),this.left.pop()}push(t){this.setCursor(Number.POSITIVE_INFINITY),this.left.push(t)}pushMany(t){this.setCursor(Number.POSITIVE_INFINITY),He(this.left,t)}unshift(t){this.setCursor(0),this.right.push(t)}unshiftMany(t){this.setCursor(0),He(this.right,t.reverse())}setCursor(t){if(!(t===this.left.length||t>this.left.length&&this.right.length===0||t<0&&this.left.length===0))if(t=4?t(l):e.interrupt(r.parser.constructs.flow,n,t)(l)}}function Sr(e,t,n,r,i,o,l,a,u){const s=u||Number.POSITIVE_INFINITY;let f=0;return c;function c(y){return y===60?(e.enter(r),e.enter(i),e.enter(o),e.consume(y),e.exit(o),p):y===null||y===32||y===41||rn(y)?n(y):(e.enter(r),e.enter(l),e.enter(a),e.enter("chunkString",{contentType:"string"}),w(y))}function p(y){return y===62?(e.enter(o),e.consume(y),e.exit(o),e.exit(i),e.exit(r),t):(e.enter(a),e.enter("chunkString",{contentType:"string"}),h(y))}function h(y){return y===62?(e.exit("chunkString"),e.exit(a),p(y)):y===null||y===60||z(y)?n(y):(e.consume(y),y===92?g:h)}function g(y){return y===60||y===62||y===92?(e.consume(y),h):h(y)}function w(y){return!f&&(y===null||y===41||W(y))?(e.exit("chunkString"),e.exit(a),e.exit(l),e.exit(r),t(y)):f999||h===null||h===91||h===93&&!u||h===94&&!a&&"_hiddenFootnoteSupport"in l.parser.constructs?n(h):h===93?(e.exit(o),e.enter(i),e.consume(h),e.exit(i),e.exit(r),t):z(h)?(e.enter("lineEnding"),e.consume(h),e.exit("lineEnding"),f):(e.enter("chunkString",{contentType:"string"}),c(h))}function c(h){return h===null||h===91||h===93||z(h)||a++>999?(e.exit("chunkString"),f(h)):(e.consume(h),u||(u=!O(h)),h===92?p:c)}function p(h){return h===91||h===92||h===93?(e.consume(h),a++,c):c(h)}}function Er(e,t,n,r,i,o){let l;return a;function a(p){return p===34||p===39||p===40?(e.enter(r),e.enter(i),e.consume(p),e.exit(i),l=p===40?41:p,u):n(p)}function u(p){return p===l?(e.enter(i),e.consume(p),e.exit(i),e.exit(r),t):(e.enter(o),s(p))}function s(p){return p===l?(e.exit(o),u(l)):p===null?n(p):z(p)?(e.enter("lineEnding"),e.consume(p),e.exit("lineEnding"),B(e,s,"linePrefix")):(e.enter("chunkString",{contentType:"string"}),f(p))}function f(p){return p===l||p===null||z(p)?(e.exit("chunkString"),s(p)):(e.consume(p),p===92?c:f)}function c(p){return p===l||p===92?(e.consume(p),f):f(p)}}function Ve(e,t){let n;return r;function r(i){return z(i)?(e.enter("lineEnding"),e.consume(i),e.exit("lineEnding"),n=!0,r):O(i)?B(e,r,n?"linePrefix":"lineSuffix")(i):t(i)}}const $l={name:"definition",tokenize:Yl},Wl={partial:!0,tokenize:Kl};function Yl(e,t,n){const r=this;let i;return o;function o(h){return e.enter("definition"),l(h)}function l(h){return Cr.call(r,e,a,n,"definitionLabel","definitionLabelMarker","definitionLabelString")(h)}function a(h){return i=ce(r.sliceSerialize(r.events[r.events.length-1][1]).slice(1,-1)),h===58?(e.enter("definitionMarker"),e.consume(h),e.exit("definitionMarker"),u):n(h)}function u(h){return W(h)?Ve(e,s)(h):s(h)}function s(h){return Sr(e,f,n,"definitionDestination","definitionDestinationLiteral","definitionDestinationLiteralMarker","definitionDestinationRaw","definitionDestinationString")(h)}function f(h){return e.attempt(Wl,c,c)(h)}function c(h){return O(h)?B(e,p,"whitespace")(h):p(h)}function p(h){return h===null||z(h)?(e.exit("definition"),r.parser.defined.push(i),t(h)):n(h)}}function Kl(e,t,n){return r;function r(a){return W(a)?Ve(e,i)(a):n(a)}function i(a){return Er(e,o,n,"definitionTitle","definitionTitleMarker","definitionTitleString")(a)}function o(a){return O(a)?B(e,l,"whitespace")(a):l(a)}function l(a){return a===null||z(a)?t(a):n(a)}}const Ql={name:"hardBreakEscape",tokenize:Xl};function Xl(e,t,n){return r;function r(o){return e.enter("hardBreakEscape"),e.consume(o),i}function i(o){return z(o)?(e.exit("hardBreakEscape"),t(o)):n(o)}}const Gl={name:"headingAtx",resolve:Jl,tokenize:Zl};function Jl(e,t){let n=e.length-2,r=3,i,o;return e[r][1].type==="whitespace"&&(r+=2),n-2>r&&e[n][1].type==="whitespace"&&(n-=2),e[n][1].type==="atxHeadingSequence"&&(r===n-1||n-4>r&&e[n-2][1].type==="whitespace")&&(n-=r+1===n?2:4),n>r&&(i={type:"atxHeadingText",start:e[r][1].start,end:e[n][1].end},o={type:"chunkText",start:e[r][1].start,end:e[n][1].end,contentType:"text"},ie(e,r,n-r+1,[["enter",i,t],["enter",o,t],["exit",o,t],["exit",i,t]])),e}function Zl(e,t,n){let r=0;return i;function i(f){return e.enter("atxHeading"),o(f)}function o(f){return e.enter("atxHeadingSequence"),l(f)}function l(f){return f===35&&r++<6?(e.consume(f),l):f===null||W(f)?(e.exit("atxHeadingSequence"),a(f)):n(f)}function a(f){return f===35?(e.enter("atxHeadingSequence"),u(f)):f===null||z(f)?(e.exit("atxHeading"),t(f)):O(f)?B(e,a,"whitespace")(f):(e.enter("atxHeadingText"),s(f))}function u(f){return f===35?(e.consume(f),u):(e.exit("atxHeadingSequence"),a(f))}function s(f){return f===null||f===35||W(f)?(e.exit("atxHeadingText"),a(f)):(e.consume(f),s)}}const eo=["address","article","aside","base","basefont","blockquote","body","caption","center","col","colgroup","dd","details","dialog","dir","div","dl","dt","fieldset","figcaption","figure","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hr","html","iframe","legend","li","link","main","menu","menuitem","nav","noframes","ol","optgroup","option","p","param","search","section","summary","table","tbody","td","tfoot","th","thead","title","tr","track","ul"],vt=["pre","script","style","textarea"],no={concrete:!0,name:"htmlFlow",resolveTo:io,tokenize:lo},to={partial:!0,tokenize:ao},ro={partial:!0,tokenize:oo};function io(e){let t=e.length;for(;t--&&!(e[t][0]==="enter"&&e[t][1].type==="htmlFlow"););return t>1&&e[t-2][1].type==="linePrefix"&&(e[t][1].start=e[t-2][1].start,e[t+1][1].start=e[t-2][1].start,e.splice(t-2,2)),e}function lo(e,t,n){const r=this;let i,o,l,a,u;return s;function s(d){return f(d)}function f(d){return e.enter("htmlFlow"),e.enter("htmlFlowData"),e.consume(d),c}function c(d){return d===33?(e.consume(d),p):d===47?(e.consume(d),o=!0,w):d===63?(e.consume(d),i=3,r.interrupt?t:m):J(d)?(e.consume(d),l=String.fromCharCode(d),S):n(d)}function p(d){return d===45?(e.consume(d),i=2,h):d===91?(e.consume(d),i=5,a=0,g):J(d)?(e.consume(d),i=4,r.interrupt?t:m):n(d)}function h(d){return d===45?(e.consume(d),r.interrupt?t:m):n(d)}function g(d){const ue="CDATA[";return d===ue.charCodeAt(a++)?(e.consume(d),a===ue.length?r.interrupt?t:P:g):n(d)}function w(d){return J(d)?(e.consume(d),l=String.fromCharCode(d),S):n(d)}function S(d){if(d===null||d===47||d===62||W(d)){const ue=d===47,we=l.toLowerCase();return!ue&&!o&&vt.includes(we)?(i=1,r.interrupt?t(d):P(d)):eo.includes(l.toLowerCase())?(i=6,ue?(e.consume(d),y):r.interrupt?t(d):P(d)):(i=7,r.interrupt&&!r.parser.lazy[r.now().line]?n(d):o?I(d):C(d))}return d===45||X(d)?(e.consume(d),l+=String.fromCharCode(d),S):n(d)}function y(d){return d===62?(e.consume(d),r.interrupt?t:P):n(d)}function I(d){return O(d)?(e.consume(d),I):k(d)}function C(d){return d===47?(e.consume(d),k):d===58||d===95||J(d)?(e.consume(d),F):O(d)?(e.consume(d),C):k(d)}function F(d){return d===45||d===46||d===58||d===95||X(d)?(e.consume(d),F):_(d)}function _(d){return d===61?(e.consume(d),b):O(d)?(e.consume(d),_):C(d)}function b(d){return d===null||d===60||d===61||d===62||d===96?n(d):d===34||d===39?(e.consume(d),u=d,M):O(d)?(e.consume(d),b):H(d)}function M(d){return d===u?(e.consume(d),u=null,j):d===null||z(d)?n(d):(e.consume(d),M)}function H(d){return d===null||d===34||d===39||d===47||d===60||d===61||d===62||d===96||W(d)?_(d):(e.consume(d),H)}function j(d){return d===47||d===62||O(d)?C(d):n(d)}function k(d){return d===62?(e.consume(d),v):n(d)}function v(d){return d===null||z(d)?P(d):O(d)?(e.consume(d),v):n(d)}function P(d){return d===45&&i===2?(e.consume(d),q):d===60&&i===1?(e.consume(d),K):d===62&&i===4?(e.consume(d),ae):d===63&&i===3?(e.consume(d),m):d===93&&i===5?(e.consume(d),pe):z(d)&&(i===6||i===7)?(e.exit("htmlFlowData"),e.check(to,me,U)(d)):d===null||z(d)?(e.exit("htmlFlowData"),U(d)):(e.consume(d),P)}function U(d){return e.check(ro,D,me)(d)}function D(d){return e.enter("lineEnding"),e.consume(d),e.exit("lineEnding"),A}function A(d){return d===null||z(d)?U(d):(e.enter("htmlFlowData"),P(d))}function q(d){return d===45?(e.consume(d),m):P(d)}function K(d){return d===47?(e.consume(d),l="",oe):P(d)}function oe(d){if(d===62){const ue=l.toLowerCase();return vt.includes(ue)?(e.consume(d),ae):P(d)}return J(d)&&l.length<8?(e.consume(d),l+=String.fromCharCode(d),oe):P(d)}function pe(d){return d===93?(e.consume(d),m):P(d)}function m(d){return d===62?(e.consume(d),ae):d===45&&i===2?(e.consume(d),m):P(d)}function ae(d){return d===null||z(d)?(e.exit("htmlFlowData"),me(d)):(e.consume(d),ae)}function me(d){return e.exit("htmlFlow"),t(d)}}function oo(e,t,n){const r=this;return i;function i(l){return z(l)?(e.enter("lineEnding"),e.consume(l),e.exit("lineEnding"),o):n(l)}function o(l){return r.parser.lazy[r.now().line]?n(l):t(l)}}function ao(e,t,n){return r;function r(i){return e.enter("lineEnding"),e.consume(i),e.exit("lineEnding"),e.attempt(Qe,t,n)}}const uo={name:"htmlText",tokenize:so};function so(e,t,n){const r=this;let i,o,l;return a;function a(m){return e.enter("htmlText"),e.enter("htmlTextData"),e.consume(m),u}function u(m){return m===33?(e.consume(m),s):m===47?(e.consume(m),_):m===63?(e.consume(m),C):J(m)?(e.consume(m),H):n(m)}function s(m){return m===45?(e.consume(m),f):m===91?(e.consume(m),o=0,g):J(m)?(e.consume(m),I):n(m)}function f(m){return m===45?(e.consume(m),h):n(m)}function c(m){return m===null?n(m):m===45?(e.consume(m),p):z(m)?(l=c,K(m)):(e.consume(m),c)}function p(m){return m===45?(e.consume(m),h):c(m)}function h(m){return m===62?q(m):m===45?p(m):c(m)}function g(m){const ae="CDATA[";return m===ae.charCodeAt(o++)?(e.consume(m),o===ae.length?w:g):n(m)}function w(m){return m===null?n(m):m===93?(e.consume(m),S):z(m)?(l=w,K(m)):(e.consume(m),w)}function S(m){return m===93?(e.consume(m),y):w(m)}function y(m){return m===62?q(m):m===93?(e.consume(m),y):w(m)}function I(m){return m===null||m===62?q(m):z(m)?(l=I,K(m)):(e.consume(m),I)}function C(m){return m===null?n(m):m===63?(e.consume(m),F):z(m)?(l=C,K(m)):(e.consume(m),C)}function F(m){return m===62?q(m):C(m)}function _(m){return J(m)?(e.consume(m),b):n(m)}function b(m){return m===45||X(m)?(e.consume(m),b):M(m)}function M(m){return z(m)?(l=M,K(m)):O(m)?(e.consume(m),M):q(m)}function H(m){return m===45||X(m)?(e.consume(m),H):m===47||m===62||W(m)?j(m):n(m)}function j(m){return m===47?(e.consume(m),q):m===58||m===95||J(m)?(e.consume(m),k):z(m)?(l=j,K(m)):O(m)?(e.consume(m),j):q(m)}function k(m){return m===45||m===46||m===58||m===95||X(m)?(e.consume(m),k):v(m)}function v(m){return m===61?(e.consume(m),P):z(m)?(l=v,K(m)):O(m)?(e.consume(m),v):j(m)}function P(m){return m===null||m===60||m===61||m===62||m===96?n(m):m===34||m===39?(e.consume(m),i=m,U):z(m)?(l=P,K(m)):O(m)?(e.consume(m),P):(e.consume(m),D)}function U(m){return m===i?(e.consume(m),i=void 0,A):m===null?n(m):z(m)?(l=U,K(m)):(e.consume(m),U)}function D(m){return m===null||m===34||m===39||m===60||m===61||m===96?n(m):m===47||m===62||W(m)?j(m):(e.consume(m),D)}function A(m){return m===47||m===62||W(m)?j(m):n(m)}function q(m){return m===62?(e.consume(m),e.exit("htmlTextData"),e.exit("htmlText"),t):n(m)}function K(m){return e.exit("htmlTextData"),e.enter("lineEnding"),e.consume(m),e.exit("lineEnding"),oe}function oe(m){return O(m)?B(e,pe,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(m):pe(m)}function pe(m){return e.enter("htmlTextData"),l(m)}}const Qn={name:"labelEnd",resolveAll:po,resolveTo:mo,tokenize:go},co={tokenize:yo},fo={tokenize:ko},ho={tokenize:xo};function po(e){let t=-1;const n=[];for(;++t=3&&(s===null||z(s))?(e.exit("thematicBreak"),t(s)):n(s)}function u(s){return s===i?(e.consume(s),r++,u):(e.exit("thematicBreakSequence"),O(s)?B(e,a,"whitespace")(s):a(s))}}const Z={continuation:{tokenize:Po},exit:Do,name:"list",tokenize:vo},To={partial:!0,tokenize:Lo},Ao={partial:!0,tokenize:zo};function vo(e,t,n){const r=this,i=r.events[r.events.length-1];let o=i&&i[1].type==="linePrefix"?i[2].sliceSerialize(i[1],!0).length:0,l=0;return a;function a(h){const g=r.containerState.type||(h===42||h===43||h===45?"listUnordered":"listOrdered");if(g==="listUnordered"?!r.containerState.marker||h===r.containerState.marker:Fn(h)){if(r.containerState.type||(r.containerState.type=g,e.enter(g,{_container:!0})),g==="listUnordered")return e.enter("listItemPrefix"),h===42||h===45?e.check(tn,n,s)(h):s(h);if(!r.interrupt||h===49)return e.enter("listItemPrefix"),e.enter("listItemValue"),u(h)}return n(h)}function u(h){return Fn(h)&&++l<10?(e.consume(h),u):(!r.interrupt||l<2)&&(r.containerState.marker?h===r.containerState.marker:h===41||h===46)?(e.exit("listItemValue"),s(h)):n(h)}function s(h){return e.enter("listItemMarker"),e.consume(h),e.exit("listItemMarker"),r.containerState.marker=r.containerState.marker||h,e.check(Qe,r.interrupt?n:f,e.attempt(To,p,c))}function f(h){return r.containerState.initialBlankLine=!0,o++,p(h)}function c(h){return O(h)?(e.enter("listItemPrefixWhitespace"),e.consume(h),e.exit("listItemPrefixWhitespace"),p):n(h)}function p(h){return r.containerState.size=o+r.sliceSerialize(e.exit("listItemPrefix"),!0).length,t(h)}}function Po(e,t,n){const r=this;return r.containerState._closeFlow=void 0,e.check(Qe,i,o);function i(a){return r.containerState.furtherBlankLines=r.containerState.furtherBlankLines||r.containerState.initialBlankLine,B(e,t,"listItemIndent",r.containerState.size+1)(a)}function o(a){return r.containerState.furtherBlankLines||!O(a)?(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,l(a)):(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,e.attempt(Ao,t,l)(a))}function l(a){return r.containerState._closeFlow=!0,r.interrupt=void 0,B(e,e.attempt(Z,t,n),"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(a)}}function zo(e,t,n){const r=this;return B(e,i,"listItemIndent",r.containerState.size+1);function i(o){const l=r.events[r.events.length-1];return l&&l[1].type==="listItemIndent"&&l[2].sliceSerialize(l[1],!0).length===r.containerState.size?t(o):n(o)}}function Do(e){e.exit(this.containerState.type)}function Lo(e,t,n){const r=this;return B(e,i,"listItemPrefixWhitespace",r.parser.constructs.disable.null.includes("codeIndented")?void 0:5);function i(o){const l=r.events[r.events.length-1];return!O(o)&&l&&l[1].type==="listItemPrefixWhitespace"?t(o):n(o)}}const Pt={name:"setextUnderline",resolveTo:Ro,tokenize:Fo};function Ro(e,t){let n=e.length,r,i,o;for(;n--;)if(e[n][0]==="enter"){if(e[n][1].type==="content"){r=n;break}e[n][1].type==="paragraph"&&(i=n)}else e[n][1].type==="content"&&e.splice(n,1),!o&&e[n][1].type==="definition"&&(o=n);const l={type:"setextHeading",start:{...e[r][1].start},end:{...e[e.length-1][1].end}};return e[i][1].type="setextHeadingText",o?(e.splice(i,0,["enter",l,t]),e.splice(o+1,0,["exit",e[r][1],t]),e[r][1].end={...e[o][1].end}):e[r][1]=l,e.push(["exit",l,t]),e}function Fo(e,t,n){const r=this;let i;return o;function o(s){let f=r.events.length,c;for(;f--;)if(r.events[f][1].type!=="lineEnding"&&r.events[f][1].type!=="linePrefix"&&r.events[f][1].type!=="content"){c=r.events[f][1].type==="paragraph";break}return!r.parser.lazy[r.now().line]&&(r.interrupt||c)?(e.enter("setextHeadingLine"),i=s,l(s)):n(s)}function l(s){return e.enter("setextHeadingLineSequence"),a(s)}function a(s){return s===i?(e.consume(s),a):(e.exit("setextHeadingLineSequence"),O(s)?B(e,u,"lineSuffix")(s):u(s))}function u(s){return s===null||z(s)?(e.exit("setextHeadingLine"),t(s)):n(s)}}const _o={tokenize:Oo};function Oo(e){const t=this,n=e.attempt(Qe,r,e.attempt(this.parser.constructs.flowInitial,i,B(e,e.attempt(this.parser.constructs.flow,i,e.attempt(jl,i)),"linePrefix")));return n;function r(o){if(o===null){e.consume(o);return}return e.enter("lineEndingBlank"),e.consume(o),e.exit("lineEndingBlank"),t.currentConstruct=void 0,n}function i(o){if(o===null){e.consume(o);return}return e.enter("lineEnding"),e.consume(o),e.exit("lineEnding"),t.currentConstruct=void 0,n}}const Mo={resolveAll:Tr()},No=Ir("string"),Bo=Ir("text");function Ir(e){return{resolveAll:Tr(e==="text"?jo:void 0),tokenize:t};function t(n){const r=this,i=this.parser.constructs[e],o=n.attempt(i,l,a);return l;function l(f){return s(f)?o(f):a(f)}function a(f){if(f===null){n.consume(f);return}return n.enter("data"),n.consume(f),u}function u(f){return s(f)?(n.exit("data"),o(f)):(n.consume(f),u)}function s(f){if(f===null)return!0;const c=i[f];let p=-1;if(c)for(;++p-1){const a=l[0];typeof a=="string"?l[0]=a.slice(r):l.shift()}o>0&&l.push(e[i].slice(0,o))}return l}function Zo(e,t){let n=-1;const r=[];let i;for(;++n0){const se=F.tokenStack[F.tokenStack.length-1];(se[1]||Dt).call(F,void 0,se[0])}for(T.position={start:xe(x.length>0?x[0][1].start:{line:1,column:1,offset:0}),end:xe(x.length>0?x[x.length-2][1].end:{line:1,column:1,offset:0})},V=-1;++V0){const se=R.tokenStack[R.tokenStack.length-1];(se[1]||Dt).call(R,void 0,se[0])}for(T.position={start:xe(x.length>0?x[0][1].start:{line:1,column:1,offset:0}),end:xe(x.length>0?x[x.length-2][1].end:{line:1,column:1,offset:0})},V=-1;++V0&&(r.className=["language-"+i[0]]);let o={type:"element",tagName:"code",properties:r,children:[{type:"text",value:n}]};return t.meta&&(o.data={meta:t.meta}),e.patch(t,o),o=e.applyData(t,o),o={type:"element",tagName:"pre",properties:{},children:[o]},e.patch(t,o),o}function pa(e,t){const n={type:"element",tagName:"del",properties:{},children:e.all(t)};return e.patch(t,n),e.applyData(t,n)}function ma(e,t){const n={type:"element",tagName:"em",properties:{},children:e.all(t)};return e.patch(t,n),e.applyData(t,n)}function da(e,t){const n=typeof e.options.clobberPrefix=="string"?e.options.clobberPrefix:"user-content-",r=String(t.identifier).toUpperCase(),i=_e(r.toLowerCase()),o=e.footnoteOrder.indexOf(r);let l,a=e.footnoteCounts.get(r);a===void 0?(a=0,e.footnoteOrder.push(r),l=e.footnoteOrder.length):l=o+1,a+=1,e.footnoteCounts.set(r,a);const u={type:"element",tagName:"a",properties:{href:"#"+n+"fn-"+i,id:n+"fnref-"+i+(a>1?"-"+a:""),dataFootnoteRef:!0,ariaDescribedBy:["footnote-label"]},children:[{type:"text",value:String(l)}]};e.patch(t,u);const s={type:"element",tagName:"sup",properties:{},children:[u]};return e.patch(t,s),e.applyData(t,s)}function ga(e,t){const n={type:"element",tagName:"h"+t.depth,properties:{},children:e.all(t)};return e.patch(t,n),e.applyData(t,n)}function ya(e,t){if(e.options.allowDangerousHtml){const n={type:"raw",value:t.value};return e.patch(t,n),e.applyData(t,n)}}function Pr(e,t){const n=t.referenceType;let r="]";if(n==="collapsed"?r+="[]":n==="full"&&(r+="["+(t.label||t.identifier)+"]"),t.type==="imageReference")return[{type:"text",value:"!["+t.alt+r}];const i=e.all(t),o=i[0];o&&o.type==="text"?o.value="["+o.value:i.unshift({type:"text",value:"["});const l=i[i.length-1];return l&&l.type==="text"?l.value+=r:i.push({type:"text",value:r}),i}function ka(e,t){const n=String(t.identifier).toUpperCase(),r=e.definitionById.get(n);if(!r)return Pr(e,t);const i={src:_e(r.url||""),alt:t.alt};r.title!==null&&r.title!==void 0&&(i.title=r.title);const o={type:"element",tagName:"img",properties:i,children:[]};return e.patch(t,o),e.applyData(t,o)}function xa(e,t){const n={src:_e(t.url)};t.alt!==null&&t.alt!==void 0&&(n.alt=t.alt),t.title!==null&&t.title!==void 0&&(n.title=t.title);const r={type:"element",tagName:"img",properties:n,children:[]};return e.patch(t,r),e.applyData(t,r)}function ba(e,t){const n={type:"text",value:t.value.replace(/\r?\n|\r/g," ")};e.patch(t,n);const r={type:"element",tagName:"code",properties:{},children:[n]};return e.patch(t,r),e.applyData(t,r)}function wa(e,t){const n=String(t.identifier).toUpperCase(),r=e.definitionById.get(n);if(!r)return Pr(e,t);const i={href:_e(r.url||"")};r.title!==null&&r.title!==void 0&&(i.title=r.title);const o={type:"element",tagName:"a",properties:i,children:e.all(t)};return e.patch(t,o),e.applyData(t,o)}function Sa(e,t){const n={href:_e(t.url)};t.title!==null&&t.title!==void 0&&(n.title=t.title);const r={type:"element",tagName:"a",properties:n,children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function Ca(e,t,n){const r=e.all(t),i=n?Ea(n):zr(t),o={},l=[];if(typeof t.checked=="boolean"){const f=r[0];let c;f&&f.type==="element"&&f.tagName==="p"?c=f:(c={type:"element",tagName:"p",properties:{},children:[]},r.unshift(c)),c.children.length>0&&c.children.unshift({type:"text",value:" "}),c.children.unshift({type:"element",tagName:"input",properties:{type:"checkbox",checked:t.checked,disabled:!0},children:[]}),o.className=["task-list-item"]}let a=-1;for(;++a1}function Ia(e,t){const n={},r=e.all(t);let i=-1;for(typeof t.start=="number"&&t.start!==1&&(n.start=t.start);++i0){const l={type:"element",tagName:"tbody",properties:{},children:e.wrap(n,!0)},a=qn(t.children[1]),u=sr(t.children[t.children.length-1]);a&&u&&(l.position={start:a,end:u}),i.push(l)}const o={type:"element",tagName:"table",properties:{},children:e.wrap(i,!0)};return e.patch(t,o),e.applyData(t,o)}function za(e,t,n){const r=n?n.children:void 0,o=(r?r.indexOf(t):1)===0?"th":"td",l=n&&n.type==="table"?n.align:void 0,a=l?l.length:t.children.length;let u=-1;const s=[];for(;++u0,!0),r[0]),i=r.index+r[0].length,r=n.exec(t);return o.push(Rt(t.slice(i),i>0,!1)),o.join("")}function Rt(e,t,n){let r=0,i=e.length;if(t){let o=e.codePointAt(r);for(;o===Lt||o===Ft;)r++,o=e.codePointAt(r)}if(n){let o=e.codePointAt(i-1);for(;o===Lt||o===Ft;)i--,o=e.codePointAt(i-1)}return i>r?e.slice(r,i):""}function Fa(e,t){const n={type:"text",value:La(String(t.value))};return e.patch(t,n),e.applyData(t,n)}function Ra(e,t){const n={type:"element",tagName:"hr",properties:{},children:[]};return e.patch(t,n),e.applyData(t,n)}const _a={blockquote:ca,break:fa,code:ha,delete:pa,emphasis:ma,footnoteReference:da,heading:ga,html:ya,imageReference:ka,image:xa,inlineCode:ba,linkReference:wa,link:Sa,listItem:Ca,list:Ia,paragraph:Ta,root:Aa,strong:va,table:Pa,tableCell:Da,tableRow:za,text:Fa,thematicBreak:Ra,toml:Je,yaml:Je,definition:Je,footnoteDefinition:Je};function Je(){}const Dr=-1,cn=0,$e=1,ln=2,Xn=3,Gn=4,Jn=5,Zn=6,Lr=7,Fr=8,Rr=typeof self=="object"?self:globalThis,_t=(e,t)=>{switch(e){case"Function":case"SharedWorker":case"Worker":case"eval":case"setInterval":case"setTimeout":throw new TypeError("unable to deserialize "+e)}return new Rr[e](t)},Oa=(e,t)=>{const n=(i,o)=>(e.set(o,i),i),r=i=>{if(e.has(i))return e.get(i);const[o,l]=t[i];switch(o){case cn:case Dr:return n(l,i);case $e:{const a=n([],i);for(const u of l)a.push(r(u));return a}case ln:{const a=n({},i);for(const[u,s]of l)a[r(u)]=r(s);return a}case Xn:return n(new Date(l),i);case Gn:{const{source:a,flags:u}=l;return n(new RegExp(a,u),i)}case Jn:{const a=n(new Map,i);for(const[u,s]of l)a.set(r(u),r(s));return a}case Zn:{const a=n(new Set,i);for(const u of l)a.add(r(u));return a}case Lr:{const{name:a,message:u}=l;return n(typeof Rr[a]=="function"?_t(a,u):new Error(u),i)}case Fr:return n(BigInt(l),i);case"BigInt":return n(Object(BigInt(l)),i);case"ArrayBuffer":return n(new Uint8Array(l).buffer,l);case"DataView":{const{buffer:a}=new Uint8Array(l);return n(new DataView(a),l)}}return n(_t(o,l),i)};return r},Ot=e=>Oa(new Map,e)(0),Ee="",{toString:Ma}={},{keys:Na}=Object,Ue=e=>{const t=typeof e;if(t!=="object"||!e)return[cn,t];const n=Ma.call(e).slice(8,-1);switch(n){case"Array":return[$e,Ee];case"Object":return[ln,Ee];case"Date":return[Xn,Ee];case"RegExp":return[Gn,Ee];case"Map":return[Jn,Ee];case"Set":return[Zn,Ee];case"DataView":return[$e,n]}return n.includes("Array")?[$e,n]:e instanceof Error?[Lr,e.name||"Error"]:[ln,n]},Ze=([e,t])=>e===cn&&(t==="function"||t==="symbol"),Ba=(e,t,n,r)=>{const i=(l,a)=>{const u=r.push(l)-1;return n.set(a,u),u},o=l=>{if(n.has(l))return n.get(l);let[a,u]=Ue(l);switch(a){case cn:{let f=l;switch(u){case"bigint":a=Fr,f=l.toString();break;case"function":case"symbol":if(e)throw new TypeError("unable to serialize "+u);f=null;break;case"undefined":return i([Dr],l)}return i([a,f],l)}case $e:{if(u){let p=l;return u==="DataView"?p=new Uint8Array(l.buffer):u==="ArrayBuffer"&&(p=new Uint8Array(l)),i([u,[...p]],l)}const f=[],c=i([a,f],l);for(const p of l)f.push(o(p));return c}case ln:{if(u)switch(u){case"BigInt":return i([u,l.toString()],l);case"Boolean":case"Number":case"String":return i([u,l.valueOf()],l)}if(t&&"toJSON"in l)return o(l.toJSON());const f=[],c=i([a,f],l);for(const p of Na(l))(e||!Ze(Ue(l[p])))&&f.push([o(p),o(l[p])]);return c}case Xn:return i([a,isNaN(l.getTime())?Ee:l.toISOString()],l);case Gn:{const{source:f,flags:c}=l;return i([a,{source:f,flags:c}],l)}case Jn:{const f=[],c=i([a,f],l);for(const[p,h]of l)(e||!(Ze(Ue(p))||Ze(Ue(h))))&&f.push([o(p),o(h)]);return c}case Zn:{const f=[],c=i([a,f],l);for(const p of l)(e||!Ze(Ue(p)))&&f.push(o(p));return c}}const{message:s}=l;return i([a,{name:u,message:s}],l)};return o},Mt=(e,{json:t,lossy:n}={})=>{const r=[];return Ba(!(t||n),!!t,new Map,r)(e),r},on=typeof structuredClone=="function"?(e,t)=>t&&("json"in t||"lossy"in t)?Ot(Mt(e,t)):structuredClone(e):(e,t)=>Ot(Mt(e,t));function ja(e,t){const n=[{type:"text",value:"↩"}];return t>1&&n.push({type:"element",tagName:"sup",properties:{},children:[{type:"text",value:String(t)}]}),n}function Ha(e,t){return"Back to reference "+(e+1)+(t>1?"-"+t:"")}function Ua(e){const t=typeof e.options.clobberPrefix=="string"?e.options.clobberPrefix:"user-content-",n=e.options.footnoteBackContent||ja,r=e.options.footnoteBackLabel||Ha,i=e.options.footnoteLabel||"Footnotes",o=e.options.footnoteLabelTagName||"h2",l=e.options.footnoteLabelProperties||{className:["sr-only"]},a=[];let u=-1;for(;++u0&&g.push({type:"text",value:" "});let I=typeof n=="string"?n:n(u,h);typeof I=="string"&&(I={type:"text",value:I}),g.push({type:"element",tagName:"a",properties:{href:"#"+t+"fnref-"+p+(h>1?"-"+h:""),dataFootnoteBackref:"",ariaLabel:typeof r=="string"?r:r(u,h),className:["data-footnote-backref"]},children:Array.isArray(I)?I:[I]})}const S=f[f.length-1];if(S&&S.type==="element"&&S.tagName==="p"){const I=S.children[S.children.length-1];I&&I.type==="text"?I.value+=" ":S.children.push({type:"text",value:" "}),S.children.push(...g)}else f.push(...g);const y={type:"element",tagName:"li",properties:{id:t+"fn-"+p},children:e.wrap(f,!0)};e.patch(s,y),a.push(y)}if(a.length!==0)return{type:"element",tagName:"section",properties:{dataFootnotes:!0,className:["footnotes"]},children:[{type:"element",tagName:o,properties:{...on(l),id:"footnote-label"},children:[{type:"text",value:i}]},{type:"text",value:` +`});const s={type:"element",tagName:"li",properties:o,children:l};return e.patch(t,s),e.applyData(t,s)}function Ea(e){let t=!1;if(e.type==="list"){t=e.spread||!1;const n=e.children;let r=-1;for(;!t&&++r1}function Ia(e,t){const n={},r=e.all(t);let i=-1;for(typeof t.start=="number"&&t.start!==1&&(n.start=t.start);++i0){const l={type:"element",tagName:"tbody",properties:{},children:e.wrap(n,!0)},a=qn(t.children[1]),u=sr(t.children[t.children.length-1]);a&&u&&(l.position={start:a,end:u}),i.push(l)}const o={type:"element",tagName:"table",properties:{},children:e.wrap(i,!0)};return e.patch(t,o),e.applyData(t,o)}function za(e,t,n){const r=n?n.children:void 0,o=(r?r.indexOf(t):1)===0?"th":"td",l=n&&n.type==="table"?n.align:void 0,a=l?l.length:t.children.length;let u=-1;const s=[];for(;++u0,!0),r[0]),i=r.index+r[0].length,r=n.exec(t);return o.push(Ft(t.slice(i),i>0,!1)),o.join("")}function Ft(e,t,n){let r=0,i=e.length;if(t){let o=e.codePointAt(r);for(;o===Lt||o===Rt;)r++,o=e.codePointAt(r)}if(n){let o=e.codePointAt(i-1);for(;o===Lt||o===Rt;)i--,o=e.codePointAt(i-1)}return i>r?e.slice(r,i):""}function Ra(e,t){const n={type:"text",value:La(String(t.value))};return e.patch(t,n),e.applyData(t,n)}function Fa(e,t){const n={type:"element",tagName:"hr",properties:{},children:[]};return e.patch(t,n),e.applyData(t,n)}const _a={blockquote:ca,break:fa,code:ha,delete:pa,emphasis:ma,footnoteReference:da,heading:ga,html:ya,imageReference:ka,image:xa,inlineCode:ba,linkReference:wa,link:Sa,listItem:Ca,list:Ia,paragraph:Ta,root:Aa,strong:va,table:Pa,tableCell:Da,tableRow:za,text:Ra,thematicBreak:Fa,toml:Je,yaml:Je,definition:Je,footnoteDefinition:Je};function Je(){}const Dr=-1,cn=0,$e=1,ln=2,Xn=3,Gn=4,Jn=5,Zn=6,Lr=7,Rr=8,Fr=typeof self=="object"?self:globalThis,_t=(e,t)=>{switch(e){case"Function":case"SharedWorker":case"Worker":case"eval":case"setInterval":case"setTimeout":throw new TypeError("unable to deserialize "+e)}return new Fr[e](t)},Oa=(e,t)=>{const n=(i,o)=>(e.set(o,i),i),r=i=>{if(e.has(i))return e.get(i);const[o,l]=t[i];switch(o){case cn:case Dr:return n(l,i);case $e:{const a=n([],i);for(const u of l)a.push(r(u));return a}case ln:{const a=n({},i);for(const[u,s]of l)a[r(u)]=r(s);return a}case Xn:return n(new Date(l),i);case Gn:{const{source:a,flags:u}=l;return n(new RegExp(a,u),i)}case Jn:{const a=n(new Map,i);for(const[u,s]of l)a.set(r(u),r(s));return a}case Zn:{const a=n(new Set,i);for(const u of l)a.add(r(u));return a}case Lr:{const{name:a,message:u}=l;return n(typeof Fr[a]=="function"?_t(a,u):new Error(u),i)}case Rr:return n(BigInt(l),i);case"BigInt":return n(Object(BigInt(l)),i);case"ArrayBuffer":return n(new Uint8Array(l).buffer,l);case"DataView":{const{buffer:a}=new Uint8Array(l);return n(new DataView(a),l)}}return n(_t(o,l),i)};return r},Ot=e=>Oa(new Map,e)(0),Ee="",{toString:Ma}={},{keys:Na}=Object,Ue=e=>{const t=typeof e;if(t!=="object"||!e)return[cn,t];const n=Ma.call(e).slice(8,-1);switch(n){case"Array":return[$e,Ee];case"Object":return[ln,Ee];case"Date":return[Xn,Ee];case"RegExp":return[Gn,Ee];case"Map":return[Jn,Ee];case"Set":return[Zn,Ee];case"DataView":return[$e,n]}return n.includes("Array")?[$e,n]:e instanceof Error?[Lr,e.name||"Error"]:[ln,n]},Ze=([e,t])=>e===cn&&(t==="function"||t==="symbol"),Ba=(e,t,n,r)=>{const i=(l,a)=>{const u=r.push(l)-1;return n.set(a,u),u},o=l=>{if(n.has(l))return n.get(l);let[a,u]=Ue(l);switch(a){case cn:{let f=l;switch(u){case"bigint":a=Rr,f=l.toString();break;case"function":case"symbol":if(e)throw new TypeError("unable to serialize "+u);f=null;break;case"undefined":return i([Dr],l)}return i([a,f],l)}case $e:{if(u){let p=l;return u==="DataView"?p=new Uint8Array(l.buffer):u==="ArrayBuffer"&&(p=new Uint8Array(l)),i([u,[...p]],l)}const f=[],c=i([a,f],l);for(const p of l)f.push(o(p));return c}case ln:{if(u)switch(u){case"BigInt":return i([u,l.toString()],l);case"Boolean":case"Number":case"String":return i([u,l.valueOf()],l)}if(t&&"toJSON"in l)return o(l.toJSON());const f=[],c=i([a,f],l);for(const p of Na(l))(e||!Ze(Ue(l[p])))&&f.push([o(p),o(l[p])]);return c}case Xn:return i([a,isNaN(l.getTime())?Ee:l.toISOString()],l);case Gn:{const{source:f,flags:c}=l;return i([a,{source:f,flags:c}],l)}case Jn:{const f=[],c=i([a,f],l);for(const[p,h]of l)(e||!(Ze(Ue(p))||Ze(Ue(h))))&&f.push([o(p),o(h)]);return c}case Zn:{const f=[],c=i([a,f],l);for(const p of l)(e||!Ze(Ue(p)))&&f.push(o(p));return c}}const{message:s}=l;return i([a,{name:u,message:s}],l)};return o},Mt=(e,{json:t,lossy:n}={})=>{const r=[];return Ba(!(t||n),!!t,new Map,r)(e),r},on=typeof structuredClone=="function"?(e,t)=>t&&("json"in t||"lossy"in t)?Ot(Mt(e,t)):structuredClone(e):(e,t)=>Ot(Mt(e,t));function ja(e,t){const n=[{type:"text",value:"↩"}];return t>1&&n.push({type:"element",tagName:"sup",properties:{},children:[{type:"text",value:String(t)}]}),n}function Ha(e,t){return"Back to reference "+(e+1)+(t>1?"-"+t:"")}function Ua(e){const t=typeof e.options.clobberPrefix=="string"?e.options.clobberPrefix:"user-content-",n=e.options.footnoteBackContent||ja,r=e.options.footnoteBackLabel||Ha,i=e.options.footnoteLabel||"Footnotes",o=e.options.footnoteLabelTagName||"h2",l=e.options.footnoteLabelProperties||{className:["sr-only"]},a=[];let u=-1;for(;++u0&&g.push({type:"text",value:" "});let I=typeof n=="string"?n:n(u,h);typeof I=="string"&&(I={type:"text",value:I}),g.push({type:"element",tagName:"a",properties:{href:"#"+t+"fnref-"+p+(h>1?"-"+h:""),dataFootnoteBackref:"",ariaLabel:typeof r=="string"?r:r(u,h),className:["data-footnote-backref"]},children:Array.isArray(I)?I:[I]})}const S=f[f.length-1];if(S&&S.type==="element"&&S.tagName==="p"){const I=S.children[S.children.length-1];I&&I.type==="text"?I.value+=" ":S.children.push({type:"text",value:" "}),S.children.push(...g)}else f.push(...g);const y={type:"element",tagName:"li",properties:{id:t+"fn-"+p},children:e.wrap(f,!0)};e.patch(s,y),a.push(y)}if(a.length!==0)return{type:"element",tagName:"section",properties:{dataFootnotes:!0,className:["footnotes"]},children:[{type:"element",tagName:o,properties:{...on(l),id:"footnote-label"},children:[{type:"text",value:i}]},{type:"text",value:` `},{type:"element",tagName:"ol",properties:{},children:e.wrap(a,!0)},{type:"text",value:` `}]}}const fn=(function(e){if(e==null)return Wa;if(typeof e=="function")return hn(e);if(typeof e=="object")return Array.isArray(e)?qa(e):Va(e);if(typeof e=="string")return $a(e);throw new Error("Expected function, string, or object as test")});function qa(e){const t=[];let n=-1;for(;++n":""))+")"})}return p;function p(){let h=_r,g,w,S;if((!t||o(u,s,f[f.length-1]||void 0))&&(h=Xa(n(u,f)),h[0]===On))return h;if("children"in u&&u.children){const y=u;if(y.children&&h[0]!==Qa)for(w=(r?y.children.length:-1)+l,S=f.concat(y);w>-1&&w0&&n.push({type:"text",value:` `}),n}function Nt(e){let t=0,n=e.charCodeAt(t);for(;n===9||n===32;)t++,n=e.charCodeAt(t);return e.slice(t)}function Bt(e,t){const n=Ja(e,t),r=n.one(e,void 0),i=Ua(n),o=Array.isArray(r)?{type:"root",children:r}:r||{type:"root",children:[]};return i&&o.children.push({type:"text",value:` -`},i),o}function ru(e,t){return e&&"run"in e?async function(n,r){const i=Bt(n,{file:r,...t});await e.run(i,r)}:function(n,r){return Bt(n,{file:r,...e||t})}}function jt(e){if(e)throw e}var kn,Ht;function iu(){if(Ht)return kn;Ht=1;var e=Object.prototype.hasOwnProperty,t=Object.prototype.toString,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=function(s){return typeof Array.isArray=="function"?Array.isArray(s):t.call(s)==="[object Array]"},o=function(s){if(!s||t.call(s)!=="[object Object]")return!1;var f=e.call(s,"constructor"),c=s.constructor&&s.constructor.prototype&&e.call(s.constructor.prototype,"isPrototypeOf");if(s.constructor&&!f&&!c)return!1;var p;for(p in s);return typeof p>"u"||e.call(s,p)},l=function(s,f){n&&f.name==="__proto__"?n(s,f.name,{enumerable:!0,configurable:!0,value:f.newValue,writable:!0}):s[f.name]=f.newValue},a=function(s,f){if(f==="__proto__")if(e.call(s,f)){if(r)return r(s,f).value}else return;return s[f]};return kn=function u(){var s,f,c,p,h,g,w=arguments[0],S=1,y=arguments.length,I=!1;for(typeof w=="boolean"&&(I=w,w=arguments[1]||{},S=2),(w==null||typeof w!="object"&&typeof w!="function")&&(w={});Sl.length;let u;a&&l.push(i);try{u=e.apply(this,l)}catch(s){const f=s;if(a&&n)throw f;return i(f)}a||(u&&u.then&&typeof u.then=="function"?u.then(o,i):u instanceof Error?i(u):o(u))}function i(l,...a){n||(n=!0,t(l,...a))}function o(l){i(null,l)}}const fe={basename:uu,dirname:su,extname:cu,join:fu,sep:"/"};function uu(e,t){if(t!==void 0&&typeof t!="string")throw new TypeError('"ext" argument must be a string');Xe(e);let n=0,r=-1,i=e.length,o;if(t===void 0||t.length===0||t.length>e.length){for(;i--;)if(e.codePointAt(i)===47){if(o){n=i+1;break}}else r<0&&(o=!0,r=i+1);return r<0?"":e.slice(n,r)}if(t===e)return"";let l=-1,a=t.length-1;for(;i--;)if(e.codePointAt(i)===47){if(o){n=i+1;break}}else l<0&&(o=!0,l=i+1),a>-1&&(e.codePointAt(i)===t.codePointAt(a--)?a<0&&(r=i):(a=-1,r=l));return n===r?r=l:r<0&&(r=e.length),e.slice(n,r)}function su(e){if(Xe(e),e.length===0)return".";let t=-1,n=e.length,r;for(;--n;)if(e.codePointAt(n)===47){if(r){t=n;break}}else r||(r=!0);return t<0?e.codePointAt(0)===47?"/":".":t===1&&e.codePointAt(0)===47?"//":e.slice(0,t)}function cu(e){Xe(e);let t=e.length,n=-1,r=0,i=-1,o=0,l;for(;t--;){const a=e.codePointAt(t);if(a===47){if(l){r=t+1;break}continue}n<0&&(l=!0,n=t+1),a===46?i<0?i=t:o!==1&&(o=1):i>-1&&(o=-1)}return i<0||n<0||o===0||o===1&&i===n-1&&i===r+1?"":e.slice(i,n)}function fu(...e){let t=-1,n;for(;++t0&&e.codePointAt(e.length-1)===47&&(n+="/"),t?"/"+n:n}function pu(e,t){let n="",r=0,i=-1,o=0,l=-1,a,u;for(;++l<=e.length;){if(l2){if(u=n.lastIndexOf("/"),u!==n.length-1){u<0?(n="",r=0):(n=n.slice(0,u),r=n.length-1-n.lastIndexOf("/")),i=l,o=0;continue}}else if(n.length>0){n="",r=0,i=l,o=0;continue}}t&&(n=n.length>0?n+"/..":"..",r=2)}else n.length>0?n+="/"+e.slice(i+1,l):n=e.slice(i+1,l),r=l-i-1;i=l,o=0}else a===46&&o>-1?o++:o=-1}return n}function Xe(e){if(typeof e!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(e))}const mu={cwd:du};function du(){return"/"}function Bn(e){return!!(e!==null&&typeof e=="object"&&"href"in e&&e.href&&"protocol"in e&&e.protocol&&e.auth===void 0)}function gu(e){if(typeof e=="string")e=new URL(e);else if(!Bn(e)){const t=new TypeError('The "path" argument must be of type string or an instance of URL. Received `'+e+"`");throw t.code="ERR_INVALID_ARG_TYPE",t}if(e.protocol!=="file:"){const t=new TypeError("The URL must be of scheme file");throw t.code="ERR_INVALID_URL_SCHEME",t}return yu(e)}function yu(e){if(e.hostname!==""){const r=new TypeError('File URL host must be "localhost" or empty on darwin');throw r.code="ERR_INVALID_FILE_URL_HOST",r}const t=e.pathname;let n=-1;for(;++n0){let[h,...g]=f;const w=r[p][1];Nn(w)&&Nn(h)&&(h=xn(!0,w,h)),r[p]=[s,h,...g]}}}}const wu=new nt().freeze();function Cn(e,t){if(typeof t!="function")throw new TypeError("Cannot `"+e+"` without `parser`")}function En(e,t){if(typeof t!="function")throw new TypeError("Cannot `"+e+"` without `compiler`")}function In(e,t){if(t)throw new Error("Cannot call `"+e+"` on a frozen processor.\nCreate a new processor first, by calling it: use `processor()` instead of `processor`.")}function qt(e){if(!Nn(e)||typeof e.type!="string")throw new TypeError("Expected node, got `"+e+"`")}function Vt(e,t,n){if(!n)throw new Error("`"+e+"` finished async. Use `"+t+"` instead")}function en(e){return Su(e)?e:new Mr(e)}function Su(e){return!!(e&&typeof e=="object"&&"message"in e&&"messages"in e)}function Cu(e){return typeof e=="string"||Eu(e)}function Eu(e){return!!(e&&typeof e=="object"&&"byteLength"in e&&"byteOffset"in e)}const Iu="https://github.com/remarkjs/react-markdown/blob/main/changelog.md",$t=[],Wt={allowDangerousHtml:!0},Tu=/^(https?|ircs?|mailto|xmpp)$/i,Au=[{from:"astPlugins",id:"remove-buggy-html-in-markdown-parser"},{from:"allowDangerousHtml",id:"remove-buggy-html-in-markdown-parser"},{from:"allowNode",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"allowElement"},{from:"allowedTypes",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"allowedElements"},{from:"disallowedTypes",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"disallowedElements"},{from:"escapeHtml",id:"remove-buggy-html-in-markdown-parser"},{from:"includeElementIndex",id:"#remove-includeelementindex"},{from:"includeNodeIndex",id:"change-includenodeindex-to-includeelementindex"},{from:"linkTarget",id:"remove-linktarget"},{from:"plugins",id:"change-plugins-to-remarkplugins",to:"remarkPlugins"},{from:"rawSourcePos",id:"#remove-rawsourcepos"},{from:"renderers",id:"change-renderers-to-components",to:"components"},{from:"source",id:"change-source-to-children",to:"children"},{from:"sourcePos",id:"#remove-sourcepos"},{from:"transformImageUri",id:"#add-urltransform",to:"urlTransform"},{from:"transformLinkUri",id:"#add-urltransform",to:"urlTransform"}];function vu(e){const t=Pu(e),n=zu(e);return Du(t.runSync(t.parse(n),n),e)}function Pu(e){const t=e.rehypePlugins||$t,n=e.remarkPlugins||$t,r=e.remarkRehypeOptions?{...e.remarkRehypeOptions,...Wt}:Wt;return wu().use(sa).use(n).use(ru,r).use(t)}function zu(e){const t=e.children||"",n=new Mr;return typeof t=="string"&&(n.value=t),n}function Du(e,t){const n=t.allowedElements,r=t.allowElement,i=t.components,o=t.disallowedElements,l=t.skipHtml,a=t.unwrapDisallowed,u=t.urlTransform||Lu;for(const f of Au)Object.hasOwn(t,f.from)&&(""+f.from+(f.to?"use `"+f.to+"` instead":"remove it")+Iu+f.id,void 0);return t.className&&(e={type:"element",tagName:"div",properties:{className:t.className},children:e.type==="root"?e.children:[e]}),et(e,s),$i(e,{Fragment:re.Fragment,components:i,ignoreInvalidStyle:!0,jsx:re.jsx,jsxs:re.jsxs,passKeys:!0,passNode:!0});function s(f,c,p){if(f.type==="raw"&&p&&typeof c=="number")return l?p.children.splice(c,1):p.children[c]={type:"text",value:f.value},c;if(f.type==="element"){let h;for(h in dn)if(Object.hasOwn(dn,h)&&Object.hasOwn(f.properties,h)){const g=f.properties[h],w=dn[h];(w===null||w.includes(f.tagName))&&(f.properties[h]=u(String(g||""),h,f))}}if(f.type==="element"){let h=n?!n.includes(f.tagName):o?o.includes(f.tagName):!1;if(!h&&r&&typeof c=="number"&&(h=!r(f,c,p)),h&&p&&typeof c=="number")return a&&f.children?p.children.splice(c,1,...f.children):p.children.splice(c,1),c}}}function Lu(e){const t=e.indexOf(":"),n=e.indexOf("?"),r=e.indexOf("#"),i=e.indexOf("/");return t===-1||i!==-1&&t>i||n!==-1&&t>n||r!==-1&&t>r||Tu.test(e.slice(0,t))?e:""}function Yt(e,t){const n=String(e);if(typeof t!="string")throw new TypeError("Expected character");let r=0,i=n.indexOf(t);for(;i!==-1;)r++,i=n.indexOf(t,i+t.length);return r}function Fu(e){if(typeof e!="string")throw new TypeError("Expected a string");return e.replace(/[|\\{}()[\]^$+*?.]/g,"\\$&").replace(/-/g,"\\x2d")}function Ru(e,t,n){const i=fn((n||{}).ignore||[]),o=_u(t);let l=-1;for(;++l0?{type:"text",value:b}:void 0),b===!1?p.lastIndex=R+1:(g!==R&&I.push({type:"text",value:s.value.slice(g,R)}),Array.isArray(b)?I.push(...b):b&&I.push(b),g=R+C[0].length,y=!0),!p.global)break;C=p.exec(s.value)}return y?(g?\]}]+$/.exec(e);if(!t)return[e,void 0];e=e.slice(0,t.index);let n=t[0],r=n.indexOf(")");const i=Yt(e,"(");let o=Yt(e,")");for(;r!==-1&&i>o;)e+=n.slice(0,r+1),n=n.slice(r+1),r=n.indexOf(")"),o++;return[e,n]}function Nr(e,t){const n=e.input.charCodeAt(e.index-1);return(e.index===0||Te(n)||un(n))&&(!t||n!==47)}Br.peek=is;function Xu(){this.buffer()}function Gu(e){this.enter({type:"footnoteReference",identifier:"",label:""},e)}function Ju(){this.buffer()}function Zu(e){this.enter({type:"footnoteDefinition",identifier:"",label:"",children:[]},e)}function es(e){const t=this.resume(),n=this.stack[this.stack.length-1];n.type,n.identifier=ce(this.sliceSerialize(e)).toLowerCase(),n.label=t}function ns(e){this.exit(e)}function ts(e){const t=this.resume(),n=this.stack[this.stack.length-1];n.type,n.identifier=ce(this.sliceSerialize(e)).toLowerCase(),n.label=t}function rs(e){this.exit(e)}function is(){return"["}function Br(e,t,n,r){const i=n.createTracker(r);let o=i.move("[^");const l=n.enter("footnoteReference"),a=n.enter("reference");return o+=i.move(n.safe(n.associationId(e),{after:"]",before:o})),a(),l(),o+=i.move("]"),o}function ls(){return{enter:{gfmFootnoteCallString:Xu,gfmFootnoteCall:Gu,gfmFootnoteDefinitionLabelString:Ju,gfmFootnoteDefinition:Zu},exit:{gfmFootnoteCallString:es,gfmFootnoteCall:ns,gfmFootnoteDefinitionLabelString:ts,gfmFootnoteDefinition:rs}}}function os(e){let t=!1;return e&&e.firstLineBlank&&(t=!0),{handlers:{footnoteDefinition:n,footnoteReference:Br},unsafe:[{character:"[",inConstruct:["label","phrasing","reference"]}]};function n(r,i,o,l){const a=o.createTracker(l);let u=a.move("[^");const s=o.enter("footnoteDefinition"),f=o.enter("label");return u+=a.move(o.safe(o.associationId(r),{before:u,after:"]"})),f(),u+=a.move("]:"),r.children&&r.children.length>0&&(a.shift(4),u+=a.move((t?` -`:" ")+o.indentLines(o.containerFlow(r,a.current()),t?jr:as))),s(),u}}function as(e,t,n){return t===0?e:jr(e,t,n)}function jr(e,t,n){return(n?"":" ")+e}const us=["autolink","destinationLiteral","destinationRaw","reference","titleQuote","titleApostrophe"];Hr.peek=ps;function ss(){return{canContainEols:["delete"],enter:{strikethrough:fs},exit:{strikethrough:hs}}}function cs(){return{unsafe:[{character:"~",inConstruct:"phrasing",notInConstruct:us}],handlers:{delete:Hr}}}function fs(e){this.enter({type:"delete",children:[]},e)}function hs(e){this.exit(e)}function Hr(e,t,n,r){const i=n.createTracker(r),o=n.enter("strikethrough");let l=i.move("~~");return l+=n.containerPhrasing(e,{...i.current(),before:l,after:"~"}),l+=i.move("~~"),o(),l}function ps(){return"~"}function ms(e){return e.length}function ds(e,t){const n=t||{},r=(n.align||[]).concat(),i=n.stringLength||ms,o=[],l=[],a=[],u=[];let s=0,f=-1;for(;++fs&&(s=e[f].length);++yu[y])&&(u[y]=C)}w.push(I)}l[f]=w,a[f]=S}let c=-1;if(typeof r=="object"&&"length"in r)for(;++cu[c]&&(u[c]=I),h[c]=I),p[c]=C}l.splice(1,0,p),a.splice(1,0,h),f=-1;const g=[];for(;++f"u"||e.call(s,p)},l=function(s,f){n&&f.name==="__proto__"?n(s,f.name,{enumerable:!0,configurable:!0,value:f.newValue,writable:!0}):s[f.name]=f.newValue},a=function(s,f){if(f==="__proto__")if(e.call(s,f)){if(r)return r(s,f).value}else return;return s[f]};return kn=function u(){var s,f,c,p,h,g,w=arguments[0],S=1,y=arguments.length,I=!1;for(typeof w=="boolean"&&(I=w,w=arguments[1]||{},S=2),(w==null||typeof w!="object"&&typeof w!="function")&&(w={});Sl.length;let u;a&&l.push(i);try{u=e.apply(this,l)}catch(s){const f=s;if(a&&n)throw f;return i(f)}a||(u&&u.then&&typeof u.then=="function"?u.then(o,i):u instanceof Error?i(u):o(u))}function i(l,...a){n||(n=!0,t(l,...a))}function o(l){i(null,l)}}const fe={basename:uu,dirname:su,extname:cu,join:fu,sep:"/"};function uu(e,t){if(t!==void 0&&typeof t!="string")throw new TypeError('"ext" argument must be a string');Xe(e);let n=0,r=-1,i=e.length,o;if(t===void 0||t.length===0||t.length>e.length){for(;i--;)if(e.codePointAt(i)===47){if(o){n=i+1;break}}else r<0&&(o=!0,r=i+1);return r<0?"":e.slice(n,r)}if(t===e)return"";let l=-1,a=t.length-1;for(;i--;)if(e.codePointAt(i)===47){if(o){n=i+1;break}}else l<0&&(o=!0,l=i+1),a>-1&&(e.codePointAt(i)===t.codePointAt(a--)?a<0&&(r=i):(a=-1,r=l));return n===r?r=l:r<0&&(r=e.length),e.slice(n,r)}function su(e){if(Xe(e),e.length===0)return".";let t=-1,n=e.length,r;for(;--n;)if(e.codePointAt(n)===47){if(r){t=n;break}}else r||(r=!0);return t<0?e.codePointAt(0)===47?"/":".":t===1&&e.codePointAt(0)===47?"//":e.slice(0,t)}function cu(e){Xe(e);let t=e.length,n=-1,r=0,i=-1,o=0,l;for(;t--;){const a=e.codePointAt(t);if(a===47){if(l){r=t+1;break}continue}n<0&&(l=!0,n=t+1),a===46?i<0?i=t:o!==1&&(o=1):i>-1&&(o=-1)}return i<0||n<0||o===0||o===1&&i===n-1&&i===r+1?"":e.slice(i,n)}function fu(...e){let t=-1,n;for(;++t0&&e.codePointAt(e.length-1)===47&&(n+="/"),t?"/"+n:n}function pu(e,t){let n="",r=0,i=-1,o=0,l=-1,a,u;for(;++l<=e.length;){if(l2){if(u=n.lastIndexOf("/"),u!==n.length-1){u<0?(n="",r=0):(n=n.slice(0,u),r=n.length-1-n.lastIndexOf("/")),i=l,o=0;continue}}else if(n.length>0){n="",r=0,i=l,o=0;continue}}t&&(n=n.length>0?n+"/..":"..",r=2)}else n.length>0?n+="/"+e.slice(i+1,l):n=e.slice(i+1,l),r=l-i-1;i=l,o=0}else a===46&&o>-1?o++:o=-1}return n}function Xe(e){if(typeof e!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(e))}const mu={cwd:du};function du(){return"/"}function Bn(e){return!!(e!==null&&typeof e=="object"&&"href"in e&&e.href&&"protocol"in e&&e.protocol&&e.auth===void 0)}function gu(e){if(typeof e=="string")e=new URL(e);else if(!Bn(e)){const t=new TypeError('The "path" argument must be of type string or an instance of URL. Received `'+e+"`");throw t.code="ERR_INVALID_ARG_TYPE",t}if(e.protocol!=="file:"){const t=new TypeError("The URL must be of scheme file");throw t.code="ERR_INVALID_URL_SCHEME",t}return yu(e)}function yu(e){if(e.hostname!==""){const r=new TypeError('File URL host must be "localhost" or empty on darwin');throw r.code="ERR_INVALID_FILE_URL_HOST",r}const t=e.pathname;let n=-1;for(;++n0){let[h,...g]=f;const w=r[p][1];Nn(w)&&Nn(h)&&(h=xn(!0,w,h)),r[p]=[s,h,...g]}}}}const wu=new nt().freeze();function Cn(e,t){if(typeof t!="function")throw new TypeError("Cannot `"+e+"` without `parser`")}function En(e,t){if(typeof t!="function")throw new TypeError("Cannot `"+e+"` without `compiler`")}function In(e,t){if(t)throw new Error("Cannot call `"+e+"` on a frozen processor.\nCreate a new processor first, by calling it: use `processor()` instead of `processor`.")}function qt(e){if(!Nn(e)||typeof e.type!="string")throw new TypeError("Expected node, got `"+e+"`")}function Vt(e,t,n){if(!n)throw new Error("`"+e+"` finished async. Use `"+t+"` instead")}function en(e){return Su(e)?e:new Mr(e)}function Su(e){return!!(e&&typeof e=="object"&&"message"in e&&"messages"in e)}function Cu(e){return typeof e=="string"||Eu(e)}function Eu(e){return!!(e&&typeof e=="object"&&"byteLength"in e&&"byteOffset"in e)}const Iu="https://github.com/remarkjs/react-markdown/blob/main/changelog.md",$t=[],Wt={allowDangerousHtml:!0},Tu=/^(https?|ircs?|mailto|xmpp)$/i,Au=[{from:"astPlugins",id:"remove-buggy-html-in-markdown-parser"},{from:"allowDangerousHtml",id:"remove-buggy-html-in-markdown-parser"},{from:"allowNode",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"allowElement"},{from:"allowedTypes",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"allowedElements"},{from:"disallowedTypes",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"disallowedElements"},{from:"escapeHtml",id:"remove-buggy-html-in-markdown-parser"},{from:"includeElementIndex",id:"#remove-includeelementindex"},{from:"includeNodeIndex",id:"change-includenodeindex-to-includeelementindex"},{from:"linkTarget",id:"remove-linktarget"},{from:"plugins",id:"change-plugins-to-remarkplugins",to:"remarkPlugins"},{from:"rawSourcePos",id:"#remove-rawsourcepos"},{from:"renderers",id:"change-renderers-to-components",to:"components"},{from:"source",id:"change-source-to-children",to:"children"},{from:"sourcePos",id:"#remove-sourcepos"},{from:"transformImageUri",id:"#add-urltransform",to:"urlTransform"},{from:"transformLinkUri",id:"#add-urltransform",to:"urlTransform"}];function vu(e){const t=Pu(e),n=zu(e);return Du(t.runSync(t.parse(n),n),e)}function Pu(e){const t=e.rehypePlugins||$t,n=e.remarkPlugins||$t,r=e.remarkRehypeOptions?{...e.remarkRehypeOptions,...Wt}:Wt;return wu().use(sa).use(n).use(ru,r).use(t)}function zu(e){const t=e.children||"",n=new Mr;return typeof t=="string"&&(n.value=t),n}function Du(e,t){const n=t.allowedElements,r=t.allowElement,i=t.components,o=t.disallowedElements,l=t.skipHtml,a=t.unwrapDisallowed,u=t.urlTransform||Lu;for(const f of Au)Object.hasOwn(t,f.from)&&(""+f.from+(f.to?"use `"+f.to+"` instead":"remove it")+Iu+f.id,void 0);return t.className&&(e={type:"element",tagName:"div",properties:{className:t.className},children:e.type==="root"?e.children:[e]}),et(e,s),$i(e,{Fragment:re.Fragment,components:i,ignoreInvalidStyle:!0,jsx:re.jsx,jsxs:re.jsxs,passKeys:!0,passNode:!0});function s(f,c,p){if(f.type==="raw"&&p&&typeof c=="number")return l?p.children.splice(c,1):p.children[c]={type:"text",value:f.value},c;if(f.type==="element"){let h;for(h in dn)if(Object.hasOwn(dn,h)&&Object.hasOwn(f.properties,h)){const g=f.properties[h],w=dn[h];(w===null||w.includes(f.tagName))&&(f.properties[h]=u(String(g||""),h,f))}}if(f.type==="element"){let h=n?!n.includes(f.tagName):o?o.includes(f.tagName):!1;if(!h&&r&&typeof c=="number"&&(h=!r(f,c,p)),h&&p&&typeof c=="number")return a&&f.children?p.children.splice(c,1,...f.children):p.children.splice(c,1),c}}}function Lu(e){const t=e.indexOf(":"),n=e.indexOf("?"),r=e.indexOf("#"),i=e.indexOf("/");return t===-1||i!==-1&&t>i||n!==-1&&t>n||r!==-1&&t>r||Tu.test(e.slice(0,t))?e:""}function Yt(e,t){const n=String(e);if(typeof t!="string")throw new TypeError("Expected character");let r=0,i=n.indexOf(t);for(;i!==-1;)r++,i=n.indexOf(t,i+t.length);return r}function Ru(e){if(typeof e!="string")throw new TypeError("Expected a string");return e.replace(/[|\\{}()[\]^$+*?.]/g,"\\$&").replace(/-/g,"\\x2d")}function Fu(e,t,n){const i=fn((n||{}).ignore||[]),o=_u(t);let l=-1;for(;++l0?{type:"text",value:b}:void 0),b===!1?p.lastIndex=F+1:(g!==F&&I.push({type:"text",value:s.value.slice(g,F)}),Array.isArray(b)?I.push(...b):b&&I.push(b),g=F+C[0].length,y=!0),!p.global)break;C=p.exec(s.value)}return y?(g?\]}]+$/.exec(e);if(!t)return[e,void 0];e=e.slice(0,t.index);let n=t[0],r=n.indexOf(")");const i=Yt(e,"(");let o=Yt(e,")");for(;r!==-1&&i>o;)e+=n.slice(0,r+1),n=n.slice(r+1),r=n.indexOf(")"),o++;return[e,n]}function Nr(e,t){const n=e.input.charCodeAt(e.index-1);return(e.index===0||Te(n)||un(n))&&(!t||n!==47)}Br.peek=is;function Xu(){this.buffer()}function Gu(e){this.enter({type:"footnoteReference",identifier:"",label:""},e)}function Ju(){this.buffer()}function Zu(e){this.enter({type:"footnoteDefinition",identifier:"",label:"",children:[]},e)}function es(e){const t=this.resume(),n=this.stack[this.stack.length-1];n.type,n.identifier=ce(this.sliceSerialize(e)).toLowerCase(),n.label=t}function ns(e){this.exit(e)}function ts(e){const t=this.resume(),n=this.stack[this.stack.length-1];n.type,n.identifier=ce(this.sliceSerialize(e)).toLowerCase(),n.label=t}function rs(e){this.exit(e)}function is(){return"["}function Br(e,t,n,r){const i=n.createTracker(r);let o=i.move("[^");const l=n.enter("footnoteReference"),a=n.enter("reference");return o+=i.move(n.safe(n.associationId(e),{after:"]",before:o})),a(),l(),o+=i.move("]"),o}function ls(){return{enter:{gfmFootnoteCallString:Xu,gfmFootnoteCall:Gu,gfmFootnoteDefinitionLabelString:Ju,gfmFootnoteDefinition:Zu},exit:{gfmFootnoteCallString:es,gfmFootnoteCall:ns,gfmFootnoteDefinitionLabelString:ts,gfmFootnoteDefinition:rs}}}function os(e){let t=!1;return e&&e.firstLineBlank&&(t=!0),{handlers:{footnoteDefinition:n,footnoteReference:Br},unsafe:[{character:"[",inConstruct:["label","phrasing","reference"]}]};function n(r,i,o,l){const a=o.createTracker(l);let u=a.move("[^");const s=o.enter("footnoteDefinition"),f=o.enter("label");return u+=a.move(o.safe(o.associationId(r),{before:u,after:"]"})),f(),u+=a.move("]:"),r.children&&r.children.length>0&&(a.shift(4),u+=a.move((t?` +`:" ")+o.indentLines(o.containerFlow(r,a.current()),t?jr:as))),s(),u}}function as(e,t,n){return t===0?e:jr(e,t,n)}function jr(e,t,n){return(n?"":" ")+e}const us=["autolink","destinationLiteral","destinationRaw","reference","titleQuote","titleApostrophe"];Hr.peek=ps;function ss(){return{canContainEols:["delete"],enter:{strikethrough:fs},exit:{strikethrough:hs}}}function cs(){return{unsafe:[{character:"~",inConstruct:"phrasing",notInConstruct:us}],handlers:{delete:Hr}}}function fs(e){this.enter({type:"delete",children:[]},e)}function hs(e){this.exit(e)}function Hr(e,t,n,r){const i=n.createTracker(r),o=n.enter("strikethrough");let l=i.move("~~");return l+=n.containerPhrasing(e,{...i.current(),before:l,after:"~"}),l+=i.move("~~"),o(),l}function ps(){return"~"}function ms(e){return e.length}function ds(e,t){const n=t||{},r=(n.align||[]).concat(),i=n.stringLength||ms,o=[],l=[],a=[],u=[];let s=0,f=-1;for(;++fs&&(s=e[f].length);++yu[y])&&(u[y]=C)}w.push(I)}l[f]=w,a[f]=S}let c=-1;if(typeof r=="object"&&"length"in r)for(;++cu[c]&&(u[c]=I),h[c]=I),p[c]=C}l.splice(1,0,p),a.splice(1,0,h),f=-1;const g=[];for(;++f "),o.shift(2);const l=n.indentLines(n.containerFlow(e,o.current()),ks);return i(),l}function ks(e,t,n){return">"+(n?"":" ")+e}function xs(e,t){return Qt(e,t.inConstruct,!0)&&!Qt(e,t.notInConstruct,!1)}function Qt(e,t,n){if(typeof t=="string"&&(t=[t]),!t||t.length===0)return n;let r=-1;for(;++rl&&(l=o):o=1,i=r+t.length,r=n.indexOf(t,i);return l}function ws(e,t){return!!(t.options.fences===!1&&e.value&&!e.lang&&/[^ \r\n]/.test(e.value)&&!/^[\t ]*(?:[\r\n]|$)|(?:^|[\r\n])[\t ]*$/.test(e.value))}function Ss(e){const t=e.options.fence||"`";if(t!=="`"&&t!=="~")throw new Error("Cannot serialize code with `"+t+"` for `options.fence`, expected `` ` `` or `~`");return t}function Cs(e,t,n,r){const i=Ss(n),o=e.value||"",l=i==="`"?"GraveAccent":"Tilde";if(ws(e,n)){const c=n.enter("codeIndented"),p=n.indentLines(o,Es);return c(),p}const a=n.createTracker(r),u=i.repeat(Math.max(bs(o,i)+1,3)),s=n.enter("codeFenced");let f=a.move(u);if(e.lang){const c=n.enter(`codeFencedLang${l}`);f+=a.move(n.safe(e.lang,{before:f,after:" ",encode:["`"],...a.current()})),c()}if(e.lang&&e.meta){const c=n.enter(`codeFencedMeta${l}`);f+=a.move(" "),f+=a.move(n.safe(e.meta,{before:f,after:` `,encode:["`"],...a.current()})),c()}return f+=a.move(` `),o&&(f+=a.move(o+` `)),f+=a.move(u),s(),f}function Es(e,t,n){return(n?"":" ")+e}function tt(e){const t=e.options.quote||'"';if(t!=='"'&&t!=="'")throw new Error("Cannot serialize title with `"+t+"` for `options.quote`, expected `\"`, or `'`");return t}function Is(e,t,n,r){const i=tt(n),o=i==='"'?"Quote":"Apostrophe",l=n.enter("definition");let a=n.enter("label");const u=n.createTracker(r);let s=u.move("[");return s+=u.move(n.safe(n.associationId(e),{before:s,after:"]",...u.current()})),s+=u.move("]: "),a(),!e.url||/[\0- \u007F]/.test(e.url)?(a=n.enter("destinationLiteral"),s+=u.move("<"),s+=u.move(n.safe(e.url,{before:s,after:">",...u.current()})),s+=u.move(">")):(a=n.enter("destinationRaw"),s+=u.move(n.safe(e.url,{before:s,after:e.title?" ":` -`,...u.current()}))),a(),e.title&&(a=n.enter(`title${o}`),s+=u.move(" "+i),s+=u.move(n.safe(e.title,{before:s,after:i,...u.current()})),s+=u.move(i),a()),l(),s}function Ts(e){const t=e.options.emphasis||"*";if(t!=="*"&&t!=="_")throw new Error("Cannot serialize emphasis with `"+t+"` for `options.emphasis`, expected `*`, or `_`");return t}function Ye(e){return"&#x"+e.toString(16).toUpperCase()+";"}function an(e,t,n){const r=Fe(e),i=Fe(t);return r===void 0?i===void 0?n==="_"?{inside:!0,outside:!0}:{inside:!1,outside:!1}:i===1?{inside:!0,outside:!0}:{inside:!1,outside:!0}:r===1?i===void 0?{inside:!1,outside:!1}:i===1?{inside:!0,outside:!0}:{inside:!1,outside:!1}:i===void 0?{inside:!1,outside:!1}:i===1?{inside:!0,outside:!1}:{inside:!1,outside:!1}}Ur.peek=As;function Ur(e,t,n,r){const i=Ts(n),o=n.enter("emphasis"),l=n.createTracker(r),a=l.move(i);let u=l.move(n.containerPhrasing(e,{after:i,before:a,...l.current()}));const s=u.charCodeAt(0),f=an(r.before.charCodeAt(r.before.length-1),s,i);f.inside&&(u=Ye(s)+u.slice(1));const c=u.charCodeAt(u.length-1),p=an(r.after.charCodeAt(0),c,i);p.inside&&(u=u.slice(0,-1)+Ye(c));const h=l.move(i);return o(),n.attentionEncodeSurroundingInfo={after:p.outside,before:f.outside},a+u+h}function As(e,t,n){return n.options.emphasis||"*"}function vs(e,t){let n=!1;return et(e,function(r){if("value"in r&&/\r?\n|\r/.test(r.value)||r.type==="break")return n=!0,On}),!!((!e.depth||e.depth<3)&&Yn(e)&&(t.options.setext||n))}function Ps(e,t,n,r){const i=Math.max(Math.min(6,e.depth||1),1),o=n.createTracker(r);if(vs(e,n)){const f=n.enter("headingSetext"),c=n.enter("phrasing"),p=n.containerPhrasing(e,{...o.current(),before:` +`,...u.current()}))),a(),e.title&&(a=n.enter(`title${o}`),s+=u.move(" "+i),s+=u.move(n.safe(e.title,{before:s,after:i,...u.current()})),s+=u.move(i),a()),l(),s}function Ts(e){const t=e.options.emphasis||"*";if(t!=="*"&&t!=="_")throw new Error("Cannot serialize emphasis with `"+t+"` for `options.emphasis`, expected `*`, or `_`");return t}function Ye(e){return"&#x"+e.toString(16).toUpperCase()+";"}function an(e,t,n){const r=Re(e),i=Re(t);return r===void 0?i===void 0?n==="_"?{inside:!0,outside:!0}:{inside:!1,outside:!1}:i===1?{inside:!0,outside:!0}:{inside:!1,outside:!0}:r===1?i===void 0?{inside:!1,outside:!1}:i===1?{inside:!0,outside:!0}:{inside:!1,outside:!1}:i===void 0?{inside:!1,outside:!1}:i===1?{inside:!0,outside:!1}:{inside:!1,outside:!1}}Ur.peek=As;function Ur(e,t,n,r){const i=Ts(n),o=n.enter("emphasis"),l=n.createTracker(r),a=l.move(i);let u=l.move(n.containerPhrasing(e,{after:i,before:a,...l.current()}));const s=u.charCodeAt(0),f=an(r.before.charCodeAt(r.before.length-1),s,i);f.inside&&(u=Ye(s)+u.slice(1));const c=u.charCodeAt(u.length-1),p=an(r.after.charCodeAt(0),c,i);p.inside&&(u=u.slice(0,-1)+Ye(c));const h=l.move(i);return o(),n.attentionEncodeSurroundingInfo={after:p.outside,before:f.outside},a+u+h}function As(e,t,n){return n.options.emphasis||"*"}function vs(e,t){let n=!1;return et(e,function(r){if("value"in r&&/\r?\n|\r/.test(r.value)||r.type==="break")return n=!0,On}),!!((!e.depth||e.depth<3)&&Yn(e)&&(t.options.setext||n))}function Ps(e,t,n,r){const i=Math.max(Math.min(6,e.depth||1),1),o=n.createTracker(r);if(vs(e,n)){const f=n.enter("headingSetext"),c=n.enter("phrasing"),p=n.containerPhrasing(e,{...o.current(),before:` `,after:` `});return c(),f(),p+` `+(i===1?"=":"-").repeat(p.length-(Math.max(p.lastIndexOf("\r"),p.lastIndexOf(` `))+1))}const l="#".repeat(i),a=n.enter("headingAtx"),u=n.enter("phrasing");o.move(l+" ");let s=n.containerPhrasing(e,{before:"# ",after:` -`,...o.current()});return/^[\t ]/.test(s)&&(s=Ye(s.charCodeAt(0))+s.slice(1)),s=s?l+" "+s:l,n.options.closeAtx&&(s+=" "+l),u(),a(),s}qr.peek=zs;function qr(e){return e.value||""}function zs(){return"<"}Vr.peek=Ds;function Vr(e,t,n,r){const i=tt(n),o=i==='"'?"Quote":"Apostrophe",l=n.enter("image");let a=n.enter("label");const u=n.createTracker(r);let s=u.move("![");return s+=u.move(n.safe(e.alt,{before:s,after:"]",...u.current()})),s+=u.move("]("),a(),!e.url&&e.title||/[\0- \u007F]/.test(e.url)?(a=n.enter("destinationLiteral"),s+=u.move("<"),s+=u.move(n.safe(e.url,{before:s,after:">",...u.current()})),s+=u.move(">")):(a=n.enter("destinationRaw"),s+=u.move(n.safe(e.url,{before:s,after:e.title?" ":")",...u.current()}))),a(),e.title&&(a=n.enter(`title${o}`),s+=u.move(" "+i),s+=u.move(n.safe(e.title,{before:s,after:i,...u.current()})),s+=u.move(i),a()),s+=u.move(")"),l(),s}function Ds(){return"!"}$r.peek=Ls;function $r(e,t,n,r){const i=e.referenceType,o=n.enter("imageReference");let l=n.enter("label");const a=n.createTracker(r);let u=a.move("![");const s=n.safe(e.alt,{before:u,after:"]",...a.current()});u+=a.move(s+"]["),l();const f=n.stack;n.stack=[],l=n.enter("reference");const c=n.safe(n.associationId(e),{before:u,after:"]",...a.current()});return l(),n.stack=f,o(),i==="full"||!s||s!==c?u+=a.move(c+"]"):i==="shortcut"?u=u.slice(0,-1):u+=a.move("]"),u}function Ls(){return"!"}Wr.peek=Fs;function Wr(e,t,n){let r=e.value||"",i="`",o=-1;for(;new RegExp("(^|[^`])"+i+"([^`]|$)").test(r);)i+="`";for(/[^ \r\n]/.test(r)&&(/^[ \r\n]/.test(r)&&/[ \r\n]$/.test(r)||/^`|`$/.test(r))&&(r=" "+r+" ");++o\u007F]/.test(e.url))}Kr.peek=Rs;function Kr(e,t,n,r){const i=tt(n),o=i==='"'?"Quote":"Apostrophe",l=n.createTracker(r);let a,u;if(Yr(e,n)){const f=n.stack;n.stack=[],a=n.enter("autolink");let c=l.move("<");return c+=l.move(n.containerPhrasing(e,{before:c,after:">",...l.current()})),c+=l.move(">"),a(),n.stack=f,c}a=n.enter("link"),u=n.enter("label");let s=l.move("[");return s+=l.move(n.containerPhrasing(e,{before:s,after:"](",...l.current()})),s+=l.move("]("),u(),!e.url&&e.title||/[\0- \u007F]/.test(e.url)?(u=n.enter("destinationLiteral"),s+=l.move("<"),s+=l.move(n.safe(e.url,{before:s,after:">",...l.current()})),s+=l.move(">")):(u=n.enter("destinationRaw"),s+=l.move(n.safe(e.url,{before:s,after:e.title?" ":")",...l.current()}))),u(),e.title&&(u=n.enter(`title${o}`),s+=l.move(" "+i),s+=l.move(n.safe(e.title,{before:s,after:i,...l.current()})),s+=l.move(i),u()),s+=l.move(")"),a(),s}function Rs(e,t,n){return Yr(e,n)?"<":"["}Qr.peek=_s;function Qr(e,t,n,r){const i=e.referenceType,o=n.enter("linkReference");let l=n.enter("label");const a=n.createTracker(r);let u=a.move("[");const s=n.containerPhrasing(e,{before:u,after:"]",...a.current()});u+=a.move(s+"]["),l();const f=n.stack;n.stack=[],l=n.enter("reference");const c=n.safe(n.associationId(e),{before:u,after:"]",...a.current()});return l(),n.stack=f,o(),i==="full"||!s||s!==c?u+=a.move(c+"]"):i==="shortcut"?u=u.slice(0,-1):u+=a.move("]"),u}function _s(){return"["}function rt(e){const t=e.options.bullet||"*";if(t!=="*"&&t!=="+"&&t!=="-")throw new Error("Cannot serialize items with `"+t+"` for `options.bullet`, expected `*`, `+`, or `-`");return t}function Os(e){const t=rt(e),n=e.options.bulletOther;if(!n)return t==="*"?"-":"*";if(n!=="*"&&n!=="+"&&n!=="-")throw new Error("Cannot serialize items with `"+n+"` for `options.bulletOther`, expected `*`, `+`, or `-`");if(n===t)throw new Error("Expected `bullet` (`"+t+"`) and `bulletOther` (`"+n+"`) to be different");return n}function Ms(e){const t=e.options.bulletOrdered||".";if(t!=="."&&t!==")")throw new Error("Cannot serialize items with `"+t+"` for `options.bulletOrdered`, expected `.` or `)`");return t}function Xr(e){const t=e.options.rule||"*";if(t!=="*"&&t!=="-"&&t!=="_")throw new Error("Cannot serialize rules with `"+t+"` for `options.rule`, expected `*`, `-`, or `_`");return t}function Ns(e,t,n,r){const i=n.enter("list"),o=n.bulletCurrent;let l=e.ordered?Ms(n):rt(n);const a=e.ordered?l==="."?")":".":Os(n);let u=t&&n.bulletLastUsed?l===n.bulletLastUsed:!1;if(!e.ordered){const f=e.children?e.children[0]:void 0;if((l==="*"||l==="-")&&f&&(!f.children||!f.children[0])&&n.stack[n.stack.length-1]==="list"&&n.stack[n.stack.length-2]==="listItem"&&n.stack[n.stack.length-3]==="list"&&n.stack[n.stack.length-4]==="listItem"&&n.indexStack[n.indexStack.length-1]===0&&n.indexStack[n.indexStack.length-2]===0&&n.indexStack[n.indexStack.length-3]===0&&(u=!0),Xr(n)===l&&f){let c=-1;for(;++c-1?t.start:1)+(n.options.incrementListMarker===!1?0:t.children.indexOf(e))+o);let l=o.length+1;(i==="tab"||i==="mixed"&&(t&&t.type==="list"&&t.spread||e.spread))&&(l=Math.ceil(l/4)*4);const a=n.createTracker(r);a.move(o+" ".repeat(l-o.length)),a.shift(l);const u=n.enter("listItem"),s=n.indentLines(n.containerFlow(e,a.current()),f);return u(),s;function f(c,p,h){return p?(h?"":" ".repeat(l))+c:(h?o:o+" ".repeat(l-o.length))+c}}function Hs(e,t,n,r){const i=n.enter("paragraph"),o=n.enter("phrasing"),l=n.containerPhrasing(e,r);return o(),i(),l}const Us=fn(["break","delete","emphasis","footnote","footnoteReference","image","imageReference","inlineCode","inlineMath","link","linkReference","mdxJsxTextElement","mdxTextExpression","strong","text","textDirective"]);function qs(e,t,n,r){return(e.children.some(function(l){return Us(l)})?n.containerPhrasing:n.containerFlow).call(n,e,r)}function Vs(e){const t=e.options.strong||"*";if(t!=="*"&&t!=="_")throw new Error("Cannot serialize strong with `"+t+"` for `options.strong`, expected `*`, or `_`");return t}Gr.peek=$s;function Gr(e,t,n,r){const i=Vs(n),o=n.enter("strong"),l=n.createTracker(r),a=l.move(i+i);let u=l.move(n.containerPhrasing(e,{after:i,before:a,...l.current()}));const s=u.charCodeAt(0),f=an(r.before.charCodeAt(r.before.length-1),s,i);f.inside&&(u=Ye(s)+u.slice(1));const c=u.charCodeAt(u.length-1),p=an(r.after.charCodeAt(0),c,i);p.inside&&(u=u.slice(0,-1)+Ye(c));const h=l.move(i+i);return o(),n.attentionEncodeSurroundingInfo={after:p.outside,before:f.outside},a+u+h}function $s(e,t,n){return n.options.strong||"*"}function Ws(e,t,n,r){return n.safe(e.value,r)}function Ys(e){const t=e.options.ruleRepetition||3;if(t<3)throw new Error("Cannot serialize rules with repetition `"+t+"` for `options.ruleRepetition`, expected `3` or more");return t}function Ks(e,t,n){const r=(Xr(n)+(n.options.ruleSpaces?" ":"")).repeat(Ys(n));return n.options.ruleSpaces?r.slice(0,-1):r}const Jr={blockquote:ys,break:Xt,code:Cs,definition:Is,emphasis:Ur,hardBreak:Xt,heading:Ps,html:qr,image:Vr,imageReference:$r,inlineCode:Wr,link:Kr,linkReference:Qr,list:Ns,listItem:js,paragraph:Hs,root:qs,strong:Gr,text:Ws,thematicBreak:Ks};function Qs(){return{enter:{table:Xs,tableData:Gt,tableHeader:Gt,tableRow:Js},exit:{codeText:Zs,table:Gs,tableData:Pn,tableHeader:Pn,tableRow:Pn}}}function Xs(e){const t=e._align;this.enter({type:"table",align:t.map(function(n){return n==="none"?null:n}),children:[]},e),this.data.inTable=!0}function Gs(e){this.exit(e),this.data.inTable=void 0}function Js(e){this.enter({type:"tableRow",children:[]},e)}function Pn(e){this.exit(e)}function Gt(e){this.enter({type:"tableCell",children:[]},e)}function Zs(e){let t=this.resume();this.data.inTable&&(t=t.replace(/\\([\\|])/g,ec));const n=this.stack[this.stack.length-1];n.type,n.value=t,this.exit(e)}function ec(e,t){return t==="|"?t:e}function nc(e){const t=e||{},n=t.tableCellPadding,r=t.tablePipeAlign,i=t.stringLength,o=n?" ":"|";return{unsafe:[{character:"\r",inConstruct:"tableCell"},{character:` +`,...o.current()});return/^[\t ]/.test(s)&&(s=Ye(s.charCodeAt(0))+s.slice(1)),s=s?l+" "+s:l,n.options.closeAtx&&(s+=" "+l),u(),a(),s}qr.peek=zs;function qr(e){return e.value||""}function zs(){return"<"}Vr.peek=Ds;function Vr(e,t,n,r){const i=tt(n),o=i==='"'?"Quote":"Apostrophe",l=n.enter("image");let a=n.enter("label");const u=n.createTracker(r);let s=u.move("![");return s+=u.move(n.safe(e.alt,{before:s,after:"]",...u.current()})),s+=u.move("]("),a(),!e.url&&e.title||/[\0- \u007F]/.test(e.url)?(a=n.enter("destinationLiteral"),s+=u.move("<"),s+=u.move(n.safe(e.url,{before:s,after:">",...u.current()})),s+=u.move(">")):(a=n.enter("destinationRaw"),s+=u.move(n.safe(e.url,{before:s,after:e.title?" ":")",...u.current()}))),a(),e.title&&(a=n.enter(`title${o}`),s+=u.move(" "+i),s+=u.move(n.safe(e.title,{before:s,after:i,...u.current()})),s+=u.move(i),a()),s+=u.move(")"),l(),s}function Ds(){return"!"}$r.peek=Ls;function $r(e,t,n,r){const i=e.referenceType,o=n.enter("imageReference");let l=n.enter("label");const a=n.createTracker(r);let u=a.move("![");const s=n.safe(e.alt,{before:u,after:"]",...a.current()});u+=a.move(s+"]["),l();const f=n.stack;n.stack=[],l=n.enter("reference");const c=n.safe(n.associationId(e),{before:u,after:"]",...a.current()});return l(),n.stack=f,o(),i==="full"||!s||s!==c?u+=a.move(c+"]"):i==="shortcut"?u=u.slice(0,-1):u+=a.move("]"),u}function Ls(){return"!"}Wr.peek=Rs;function Wr(e,t,n){let r=e.value||"",i="`",o=-1;for(;new RegExp("(^|[^`])"+i+"([^`]|$)").test(r);)i+="`";for(/[^ \r\n]/.test(r)&&(/^[ \r\n]/.test(r)&&/[ \r\n]$/.test(r)||/^`|`$/.test(r))&&(r=" "+r+" ");++o\u007F]/.test(e.url))}Kr.peek=Fs;function Kr(e,t,n,r){const i=tt(n),o=i==='"'?"Quote":"Apostrophe",l=n.createTracker(r);let a,u;if(Yr(e,n)){const f=n.stack;n.stack=[],a=n.enter("autolink");let c=l.move("<");return c+=l.move(n.containerPhrasing(e,{before:c,after:">",...l.current()})),c+=l.move(">"),a(),n.stack=f,c}a=n.enter("link"),u=n.enter("label");let s=l.move("[");return s+=l.move(n.containerPhrasing(e,{before:s,after:"](",...l.current()})),s+=l.move("]("),u(),!e.url&&e.title||/[\0- \u007F]/.test(e.url)?(u=n.enter("destinationLiteral"),s+=l.move("<"),s+=l.move(n.safe(e.url,{before:s,after:">",...l.current()})),s+=l.move(">")):(u=n.enter("destinationRaw"),s+=l.move(n.safe(e.url,{before:s,after:e.title?" ":")",...l.current()}))),u(),e.title&&(u=n.enter(`title${o}`),s+=l.move(" "+i),s+=l.move(n.safe(e.title,{before:s,after:i,...l.current()})),s+=l.move(i),u()),s+=l.move(")"),a(),s}function Fs(e,t,n){return Yr(e,n)?"<":"["}Qr.peek=_s;function Qr(e,t,n,r){const i=e.referenceType,o=n.enter("linkReference");let l=n.enter("label");const a=n.createTracker(r);let u=a.move("[");const s=n.containerPhrasing(e,{before:u,after:"]",...a.current()});u+=a.move(s+"]["),l();const f=n.stack;n.stack=[],l=n.enter("reference");const c=n.safe(n.associationId(e),{before:u,after:"]",...a.current()});return l(),n.stack=f,o(),i==="full"||!s||s!==c?u+=a.move(c+"]"):i==="shortcut"?u=u.slice(0,-1):u+=a.move("]"),u}function _s(){return"["}function rt(e){const t=e.options.bullet||"*";if(t!=="*"&&t!=="+"&&t!=="-")throw new Error("Cannot serialize items with `"+t+"` for `options.bullet`, expected `*`, `+`, or `-`");return t}function Os(e){const t=rt(e),n=e.options.bulletOther;if(!n)return t==="*"?"-":"*";if(n!=="*"&&n!=="+"&&n!=="-")throw new Error("Cannot serialize items with `"+n+"` for `options.bulletOther`, expected `*`, `+`, or `-`");if(n===t)throw new Error("Expected `bullet` (`"+t+"`) and `bulletOther` (`"+n+"`) to be different");return n}function Ms(e){const t=e.options.bulletOrdered||".";if(t!=="."&&t!==")")throw new Error("Cannot serialize items with `"+t+"` for `options.bulletOrdered`, expected `.` or `)`");return t}function Xr(e){const t=e.options.rule||"*";if(t!=="*"&&t!=="-"&&t!=="_")throw new Error("Cannot serialize rules with `"+t+"` for `options.rule`, expected `*`, `-`, or `_`");return t}function Ns(e,t,n,r){const i=n.enter("list"),o=n.bulletCurrent;let l=e.ordered?Ms(n):rt(n);const a=e.ordered?l==="."?")":".":Os(n);let u=t&&n.bulletLastUsed?l===n.bulletLastUsed:!1;if(!e.ordered){const f=e.children?e.children[0]:void 0;if((l==="*"||l==="-")&&f&&(!f.children||!f.children[0])&&n.stack[n.stack.length-1]==="list"&&n.stack[n.stack.length-2]==="listItem"&&n.stack[n.stack.length-3]==="list"&&n.stack[n.stack.length-4]==="listItem"&&n.indexStack[n.indexStack.length-1]===0&&n.indexStack[n.indexStack.length-2]===0&&n.indexStack[n.indexStack.length-3]===0&&(u=!0),Xr(n)===l&&f){let c=-1;for(;++c-1?t.start:1)+(n.options.incrementListMarker===!1?0:t.children.indexOf(e))+o);let l=o.length+1;(i==="tab"||i==="mixed"&&(t&&t.type==="list"&&t.spread||e.spread))&&(l=Math.ceil(l/4)*4);const a=n.createTracker(r);a.move(o+" ".repeat(l-o.length)),a.shift(l);const u=n.enter("listItem"),s=n.indentLines(n.containerFlow(e,a.current()),f);return u(),s;function f(c,p,h){return p?(h?"":" ".repeat(l))+c:(h?o:o+" ".repeat(l-o.length))+c}}function Hs(e,t,n,r){const i=n.enter("paragraph"),o=n.enter("phrasing"),l=n.containerPhrasing(e,r);return o(),i(),l}const Us=fn(["break","delete","emphasis","footnote","footnoteReference","image","imageReference","inlineCode","inlineMath","link","linkReference","mdxJsxTextElement","mdxTextExpression","strong","text","textDirective"]);function qs(e,t,n,r){return(e.children.some(function(l){return Us(l)})?n.containerPhrasing:n.containerFlow).call(n,e,r)}function Vs(e){const t=e.options.strong||"*";if(t!=="*"&&t!=="_")throw new Error("Cannot serialize strong with `"+t+"` for `options.strong`, expected `*`, or `_`");return t}Gr.peek=$s;function Gr(e,t,n,r){const i=Vs(n),o=n.enter("strong"),l=n.createTracker(r),a=l.move(i+i);let u=l.move(n.containerPhrasing(e,{after:i,before:a,...l.current()}));const s=u.charCodeAt(0),f=an(r.before.charCodeAt(r.before.length-1),s,i);f.inside&&(u=Ye(s)+u.slice(1));const c=u.charCodeAt(u.length-1),p=an(r.after.charCodeAt(0),c,i);p.inside&&(u=u.slice(0,-1)+Ye(c));const h=l.move(i+i);return o(),n.attentionEncodeSurroundingInfo={after:p.outside,before:f.outside},a+u+h}function $s(e,t,n){return n.options.strong||"*"}function Ws(e,t,n,r){return n.safe(e.value,r)}function Ys(e){const t=e.options.ruleRepetition||3;if(t<3)throw new Error("Cannot serialize rules with repetition `"+t+"` for `options.ruleRepetition`, expected `3` or more");return t}function Ks(e,t,n){const r=(Xr(n)+(n.options.ruleSpaces?" ":"")).repeat(Ys(n));return n.options.ruleSpaces?r.slice(0,-1):r}const Jr={blockquote:ys,break:Xt,code:Cs,definition:Is,emphasis:Ur,hardBreak:Xt,heading:Ps,html:qr,image:Vr,imageReference:$r,inlineCode:Wr,link:Kr,linkReference:Qr,list:Ns,listItem:js,paragraph:Hs,root:qs,strong:Gr,text:Ws,thematicBreak:Ks};function Qs(){return{enter:{table:Xs,tableData:Gt,tableHeader:Gt,tableRow:Js},exit:{codeText:Zs,table:Gs,tableData:Pn,tableHeader:Pn,tableRow:Pn}}}function Xs(e){const t=e._align;this.enter({type:"table",align:t.map(function(n){return n==="none"?null:n}),children:[]},e),this.data.inTable=!0}function Gs(e){this.exit(e),this.data.inTable=void 0}function Js(e){this.enter({type:"tableRow",children:[]},e)}function Pn(e){this.exit(e)}function Gt(e){this.enter({type:"tableCell",children:[]},e)}function Zs(e){let t=this.resume();this.data.inTable&&(t=t.replace(/\\([\\|])/g,ec));const n=this.stack[this.stack.length-1];n.type,n.value=t,this.exit(e)}function ec(e,t){return t==="|"?t:e}function nc(e){const t=e||{},n=t.tableCellPadding,r=t.tablePipeAlign,i=t.stringLength,o=n?" ":"|";return{unsafe:[{character:"\r",inConstruct:"tableCell"},{character:` `,inConstruct:"tableCell"},{atBreak:!0,character:"|",after:"[ :-]"},{character:"|",inConstruct:"tableCell"},{atBreak:!0,character:":",after:"-"},{atBreak:!0,character:"-",after:"[:|-]"}],handlers:{inlineCode:p,table:l,tableCell:u,tableRow:a}};function l(h,g,w,S){return s(f(h,w,S),h.align)}function a(h,g,w,S){const y=c(h,w,S),I=s([y]);return I.slice(0,I.indexOf(` -`))}function u(h,g,w,S){const y=w.enter("tableCell"),I=w.enter("phrasing"),C=w.containerPhrasing(h,{...S,before:o,after:o});return I(),y(),C}function s(h,g){return ds(h,{align:g,alignDelimiters:r,padding:n,stringLength:i})}function f(h,g,w){const S=h.children;let y=-1;const I=[],C=g.enter("table");for(;++y0&&!n&&(e[e.length-1][1]._gfmAutolinkLiteralWalkedInto=!0),n}const xc={tokenize:Ac,partial:!0};function bc(){return{document:{91:{name:"gfmFootnoteDefinition",tokenize:Ec,continuation:{tokenize:Ic},exit:Tc}},text:{91:{name:"gfmFootnoteCall",tokenize:Cc},93:{name:"gfmPotentialFootnoteCall",add:"after",tokenize:wc,resolveTo:Sc}}}}function wc(e,t,n){const r=this;let i=r.events.length;const o=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let l;for(;i--;){const u=r.events[i][1];if(u.type==="labelImage"){l=u;break}if(u.type==="gfmFootnoteCall"||u.type==="labelLink"||u.type==="label"||u.type==="image"||u.type==="link")break}return a;function a(u){if(!l||!l._balanced)return n(u);const s=ce(r.sliceSerialize({start:l.end,end:r.now()}));return s.codePointAt(0)!==94||!o.includes(s.slice(1))?n(u):(e.enter("gfmFootnoteCallLabelMarker"),e.consume(u),e.exit("gfmFootnoteCallLabelMarker"),t(u))}}function Sc(e,t){let n=e.length;for(;n--;)if(e[n][1].type==="labelImage"&&e[n][0]==="enter"){e[n][1];break}e[n+1][1].type="data",e[n+3][1].type="gfmFootnoteCallLabelMarker";const r={type:"gfmFootnoteCall",start:Object.assign({},e[n+3][1].start),end:Object.assign({},e[e.length-1][1].end)},i={type:"gfmFootnoteCallMarker",start:Object.assign({},e[n+3][1].end),end:Object.assign({},e[n+3][1].end)};i.end.column++,i.end.offset++,i.end._bufferIndex++;const o={type:"gfmFootnoteCallString",start:Object.assign({},i.end),end:Object.assign({},e[e.length-1][1].start)},l={type:"chunkString",contentType:"string",start:Object.assign({},o.start),end:Object.assign({},o.end)},a=[e[n+1],e[n+2],["enter",r,t],e[n+3],e[n+4],["enter",i,t],["exit",i,t],["enter",o,t],["enter",l,t],["exit",l,t],["exit",o,t],e[e.length-2],e[e.length-1],["exit",r,t]];return e.splice(n,e.length-n+1,...a),e}function Cc(e,t,n){const r=this,i=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let o=0,l;return a;function a(c){return e.enter("gfmFootnoteCall"),e.enter("gfmFootnoteCallLabelMarker"),e.consume(c),e.exit("gfmFootnoteCallLabelMarker"),u}function u(c){return c!==94?n(c):(e.enter("gfmFootnoteCallMarker"),e.consume(c),e.exit("gfmFootnoteCallMarker"),e.enter("gfmFootnoteCallString"),e.enter("chunkString").contentType="string",s)}function s(c){if(o>999||c===93&&!l||c===null||c===91||W(c))return n(c);if(c===93){e.exit("chunkString");const p=e.exit("gfmFootnoteCallString");return i.includes(ce(r.sliceSerialize(p)))?(e.enter("gfmFootnoteCallLabelMarker"),e.consume(c),e.exit("gfmFootnoteCallLabelMarker"),e.exit("gfmFootnoteCall"),t):n(c)}return W(c)||(l=!0),o++,e.consume(c),c===92?f:s}function f(c){return c===91||c===92||c===93?(e.consume(c),o++,s):s(c)}}function Ec(e,t,n){const r=this,i=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let o,l=0,a;return u;function u(g){return e.enter("gfmFootnoteDefinition")._container=!0,e.enter("gfmFootnoteDefinitionLabel"),e.enter("gfmFootnoteDefinitionLabelMarker"),e.consume(g),e.exit("gfmFootnoteDefinitionLabelMarker"),s}function s(g){return g===94?(e.enter("gfmFootnoteDefinitionMarker"),e.consume(g),e.exit("gfmFootnoteDefinitionMarker"),e.enter("gfmFootnoteDefinitionLabelString"),e.enter("chunkString").contentType="string",f):n(g)}function f(g){if(l>999||g===93&&!a||g===null||g===91||W(g))return n(g);if(g===93){e.exit("chunkString");const w=e.exit("gfmFootnoteDefinitionLabelString");return o=ce(r.sliceSerialize(w)),e.enter("gfmFootnoteDefinitionLabelMarker"),e.consume(g),e.exit("gfmFootnoteDefinitionLabelMarker"),e.exit("gfmFootnoteDefinitionLabel"),p}return W(g)||(a=!0),l++,e.consume(g),g===92?c:f}function c(g){return g===91||g===92||g===93?(e.consume(g),l++,f):f(g)}function p(g){return g===58?(e.enter("definitionMarker"),e.consume(g),e.exit("definitionMarker"),i.includes(o)||i.push(o),B(e,h,"gfmFootnoteDefinitionWhitespace")):n(g)}function h(g){return t(g)}}function Ic(e,t,n){return e.check(Qe,t,e.attempt(xc,t,n))}function Tc(e){e.exit("gfmFootnoteDefinition")}function Ac(e,t,n){const r=this;return B(e,i,"gfmFootnoteDefinitionIndent",5);function i(o){const l=r.events[r.events.length-1];return l&&l[1].type==="gfmFootnoteDefinitionIndent"&&l[2].sliceSerialize(l[1],!0).length===4?t(o):n(o)}}function vc(e){let n=(e||{}).singleTilde;const r={name:"strikethrough",tokenize:o,resolveAll:i};return n==null&&(n=!0),{text:{126:r},insideSpan:{null:[r]},attentionMarkers:{null:[126]}};function i(l,a){let u=-1;for(;++u1?u(g):(l.consume(g),c++,h);if(c<2&&!n)return u(g);const S=l.exit("strikethroughSequenceTemporary"),y=Fe(g);return S._open=!y||y===2&&!!w,S._close=!w||w===2&&!!y,a(g)}}}class Pc{constructor(){this.map=[]}add(t,n,r){zc(this,t,n,r)}consume(t){if(this.map.sort(function(o,l){return o[0]-l[0]}),this.map.length===0)return;let n=this.map.length;const r=[];for(;n>0;)n-=1,r.push(t.slice(this.map[n][0]+this.map[n][1]),this.map[n][2]),t.length=this.map[n][0];r.push(t.slice()),t.length=0;let i=r.pop();for(;i;){for(const o of i)t.push(o);i=r.pop()}this.map.length=0}}function zc(e,t,n,r){let i=0;if(!(n===0&&r.length===0)){for(;i-1;){const D=r.events[v][1].type;if(D==="lineEnding"||D==="linePrefix")v--;else break}const P=v>-1?r.events[v][1].type:null,U=P==="tableHead"||P==="tableRow"?b:u;return U===b&&r.parser.lazy[r.now().line]?n(k):U(k)}function u(k){return e.enter("tableHead"),e.enter("tableRow"),s(k)}function s(k){return k===124||(l=!0,o+=1),f(k)}function f(k){return k===null?n(k):z(k)?o>1?(o=0,r.interrupt=!0,e.exit("tableRow"),e.enter("lineEnding"),e.consume(k),e.exit("lineEnding"),h):n(k):O(k)?B(e,f,"whitespace")(k):(o+=1,l&&(l=!1,i+=1),k===124?(e.enter("tableCellDivider"),e.consume(k),e.exit("tableCellDivider"),l=!0,f):(e.enter("data"),c(k)))}function c(k){return k===null||k===124||W(k)?(e.exit("data"),f(k)):(e.consume(k),k===92?p:c)}function p(k){return k===92||k===124?(e.consume(k),c):c(k)}function h(k){return r.interrupt=!1,r.parser.lazy[r.now().line]?n(k):(e.enter("tableDelimiterRow"),l=!1,O(k)?B(e,g,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(k):g(k))}function g(k){return k===45||k===58?S(k):k===124?(l=!0,e.enter("tableCellDivider"),e.consume(k),e.exit("tableCellDivider"),w):_(k)}function w(k){return O(k)?B(e,S,"whitespace")(k):S(k)}function S(k){return k===58?(o+=1,l=!0,e.enter("tableDelimiterMarker"),e.consume(k),e.exit("tableDelimiterMarker"),y):k===45?(o+=1,y(k)):k===null||z(k)?R(k):_(k)}function y(k){return k===45?(e.enter("tableDelimiterFiller"),I(k)):_(k)}function I(k){return k===45?(e.consume(k),I):k===58?(l=!0,e.exit("tableDelimiterFiller"),e.enter("tableDelimiterMarker"),e.consume(k),e.exit("tableDelimiterMarker"),C):(e.exit("tableDelimiterFiller"),C(k))}function C(k){return O(k)?B(e,R,"whitespace")(k):R(k)}function R(k){return k===124?g(k):k===null||z(k)?!l||i!==o?_(k):(e.exit("tableDelimiterRow"),e.exit("tableHead"),t(k)):_(k)}function _(k){return n(k)}function b(k){return e.enter("tableRow"),M(k)}function M(k){return k===124?(e.enter("tableCellDivider"),e.consume(k),e.exit("tableCellDivider"),M):k===null||z(k)?(e.exit("tableRow"),t(k)):O(k)?B(e,M,"whitespace")(k):(e.enter("data"),H(k))}function H(k){return k===null||k===124||W(k)?(e.exit("data"),M(k)):(e.consume(k),k===92?j:H)}function j(k){return k===92||k===124?(e.consume(k),H):H(k)}}function Rc(e,t){let n=-1,r=!0,i=0,o=[0,0,0,0],l=[0,0,0,0],a=!1,u=0,s,f,c;const p=new Pc;for(;++nn[2]+1){const g=n[2]+1,w=n[3]-n[2]-1;e.add(g,w,[])}}e.add(n[3]+1,0,[["exit",c,t]])}return i!==void 0&&(o.end=Object.assign({},Le(t.events,i)),e.add(i,0,[["exit",o,t]]),o=void 0),o}function Zt(e,t,n,r,i){const o=[],l=Le(t.events,n);i&&(i.end=Object.assign({},l),o.push(["exit",i,t])),r.end=Object.assign({},l),o.push(["exit",r,t]),e.add(n+1,0,o)}function Le(e,t){const n=e[t],r=n[0]==="enter"?"start":"end";return n[1][r]}const _c={name:"tasklistCheck",tokenize:Mc};function Oc(){return{text:{91:_c}}}function Mc(e,t,n){const r=this;return i;function i(u){return r.previous!==null||!r._gfmTasklistFirstContentOfListItem?n(u):(e.enter("taskListCheck"),e.enter("taskListCheckMarker"),e.consume(u),e.exit("taskListCheckMarker"),o)}function o(u){return W(u)?(e.enter("taskListCheckValueUnchecked"),e.consume(u),e.exit("taskListCheckValueUnchecked"),l):u===88||u===120?(e.enter("taskListCheckValueChecked"),e.consume(u),e.exit("taskListCheckValueChecked"),l):n(u)}function l(u){return u===93?(e.enter("taskListCheckMarker"),e.consume(u),e.exit("taskListCheckMarker"),e.exit("taskListCheck"),a):n(u)}function a(u){return z(u)?t(u):O(u)?e.check({tokenize:Nc},t,n)(u):n(u)}}function Nc(e,t,n){return B(e,r,"whitespace");function r(i){return i===null?n(i):t(i)}}function Bc(e){return gr([cc(),bc(),vc(e),Lc(),Oc()])}const jc={};function Hc(e){const t=this,n=e||jc,r=t.data(),i=r.micromarkExtensions||(r.micromarkExtensions=[]),o=r.fromMarkdownExtensions||(r.fromMarkdownExtensions=[]),l=r.toMarkdownExtensions||(r.toMarkdownExtensions=[]);i.push(Bc(n)),o.push(oc()),l.push(ac(n))}const Uc=`# Admin dashboard +`))}function u(h,g,w,S){const y=w.enter("tableCell"),I=w.enter("phrasing"),C=w.containerPhrasing(h,{...S,before:o,after:o});return I(),y(),C}function s(h,g){return ds(h,{align:g,alignDelimiters:r,padding:n,stringLength:i})}function f(h,g,w){const S=h.children;let y=-1;const I=[],C=g.enter("table");for(;++y0&&!n&&(e[e.length-1][1]._gfmAutolinkLiteralWalkedInto=!0),n}const xc={tokenize:Ac,partial:!0};function bc(){return{document:{91:{name:"gfmFootnoteDefinition",tokenize:Ec,continuation:{tokenize:Ic},exit:Tc}},text:{91:{name:"gfmFootnoteCall",tokenize:Cc},93:{name:"gfmPotentialFootnoteCall",add:"after",tokenize:wc,resolveTo:Sc}}}}function wc(e,t,n){const r=this;let i=r.events.length;const o=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let l;for(;i--;){const u=r.events[i][1];if(u.type==="labelImage"){l=u;break}if(u.type==="gfmFootnoteCall"||u.type==="labelLink"||u.type==="label"||u.type==="image"||u.type==="link")break}return a;function a(u){if(!l||!l._balanced)return n(u);const s=ce(r.sliceSerialize({start:l.end,end:r.now()}));return s.codePointAt(0)!==94||!o.includes(s.slice(1))?n(u):(e.enter("gfmFootnoteCallLabelMarker"),e.consume(u),e.exit("gfmFootnoteCallLabelMarker"),t(u))}}function Sc(e,t){let n=e.length;for(;n--;)if(e[n][1].type==="labelImage"&&e[n][0]==="enter"){e[n][1];break}e[n+1][1].type="data",e[n+3][1].type="gfmFootnoteCallLabelMarker";const r={type:"gfmFootnoteCall",start:Object.assign({},e[n+3][1].start),end:Object.assign({},e[e.length-1][1].end)},i={type:"gfmFootnoteCallMarker",start:Object.assign({},e[n+3][1].end),end:Object.assign({},e[n+3][1].end)};i.end.column++,i.end.offset++,i.end._bufferIndex++;const o={type:"gfmFootnoteCallString",start:Object.assign({},i.end),end:Object.assign({},e[e.length-1][1].start)},l={type:"chunkString",contentType:"string",start:Object.assign({},o.start),end:Object.assign({},o.end)},a=[e[n+1],e[n+2],["enter",r,t],e[n+3],e[n+4],["enter",i,t],["exit",i,t],["enter",o,t],["enter",l,t],["exit",l,t],["exit",o,t],e[e.length-2],e[e.length-1],["exit",r,t]];return e.splice(n,e.length-n+1,...a),e}function Cc(e,t,n){const r=this,i=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let o=0,l;return a;function a(c){return e.enter("gfmFootnoteCall"),e.enter("gfmFootnoteCallLabelMarker"),e.consume(c),e.exit("gfmFootnoteCallLabelMarker"),u}function u(c){return c!==94?n(c):(e.enter("gfmFootnoteCallMarker"),e.consume(c),e.exit("gfmFootnoteCallMarker"),e.enter("gfmFootnoteCallString"),e.enter("chunkString").contentType="string",s)}function s(c){if(o>999||c===93&&!l||c===null||c===91||W(c))return n(c);if(c===93){e.exit("chunkString");const p=e.exit("gfmFootnoteCallString");return i.includes(ce(r.sliceSerialize(p)))?(e.enter("gfmFootnoteCallLabelMarker"),e.consume(c),e.exit("gfmFootnoteCallLabelMarker"),e.exit("gfmFootnoteCall"),t):n(c)}return W(c)||(l=!0),o++,e.consume(c),c===92?f:s}function f(c){return c===91||c===92||c===93?(e.consume(c),o++,s):s(c)}}function Ec(e,t,n){const r=this,i=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let o,l=0,a;return u;function u(g){return e.enter("gfmFootnoteDefinition")._container=!0,e.enter("gfmFootnoteDefinitionLabel"),e.enter("gfmFootnoteDefinitionLabelMarker"),e.consume(g),e.exit("gfmFootnoteDefinitionLabelMarker"),s}function s(g){return g===94?(e.enter("gfmFootnoteDefinitionMarker"),e.consume(g),e.exit("gfmFootnoteDefinitionMarker"),e.enter("gfmFootnoteDefinitionLabelString"),e.enter("chunkString").contentType="string",f):n(g)}function f(g){if(l>999||g===93&&!a||g===null||g===91||W(g))return n(g);if(g===93){e.exit("chunkString");const w=e.exit("gfmFootnoteDefinitionLabelString");return o=ce(r.sliceSerialize(w)),e.enter("gfmFootnoteDefinitionLabelMarker"),e.consume(g),e.exit("gfmFootnoteDefinitionLabelMarker"),e.exit("gfmFootnoteDefinitionLabel"),p}return W(g)||(a=!0),l++,e.consume(g),g===92?c:f}function c(g){return g===91||g===92||g===93?(e.consume(g),l++,f):f(g)}function p(g){return g===58?(e.enter("definitionMarker"),e.consume(g),e.exit("definitionMarker"),i.includes(o)||i.push(o),B(e,h,"gfmFootnoteDefinitionWhitespace")):n(g)}function h(g){return t(g)}}function Ic(e,t,n){return e.check(Qe,t,e.attempt(xc,t,n))}function Tc(e){e.exit("gfmFootnoteDefinition")}function Ac(e,t,n){const r=this;return B(e,i,"gfmFootnoteDefinitionIndent",5);function i(o){const l=r.events[r.events.length-1];return l&&l[1].type==="gfmFootnoteDefinitionIndent"&&l[2].sliceSerialize(l[1],!0).length===4?t(o):n(o)}}function vc(e){let n=(e||{}).singleTilde;const r={name:"strikethrough",tokenize:o,resolveAll:i};return n==null&&(n=!0),{text:{126:r},insideSpan:{null:[r]},attentionMarkers:{null:[126]}};function i(l,a){let u=-1;for(;++u1?u(g):(l.consume(g),c++,h);if(c<2&&!n)return u(g);const S=l.exit("strikethroughSequenceTemporary"),y=Re(g);return S._open=!y||y===2&&!!w,S._close=!w||w===2&&!!y,a(g)}}}class Pc{constructor(){this.map=[]}add(t,n,r){zc(this,t,n,r)}consume(t){if(this.map.sort(function(o,l){return o[0]-l[0]}),this.map.length===0)return;let n=this.map.length;const r=[];for(;n>0;)n-=1,r.push(t.slice(this.map[n][0]+this.map[n][1]),this.map[n][2]),t.length=this.map[n][0];r.push(t.slice()),t.length=0;let i=r.pop();for(;i;){for(const o of i)t.push(o);i=r.pop()}this.map.length=0}}function zc(e,t,n,r){let i=0;if(!(n===0&&r.length===0)){for(;i-1;){const D=r.events[v][1].type;if(D==="lineEnding"||D==="linePrefix")v--;else break}const P=v>-1?r.events[v][1].type:null,U=P==="tableHead"||P==="tableRow"?b:u;return U===b&&r.parser.lazy[r.now().line]?n(k):U(k)}function u(k){return e.enter("tableHead"),e.enter("tableRow"),s(k)}function s(k){return k===124||(l=!0,o+=1),f(k)}function f(k){return k===null?n(k):z(k)?o>1?(o=0,r.interrupt=!0,e.exit("tableRow"),e.enter("lineEnding"),e.consume(k),e.exit("lineEnding"),h):n(k):O(k)?B(e,f,"whitespace")(k):(o+=1,l&&(l=!1,i+=1),k===124?(e.enter("tableCellDivider"),e.consume(k),e.exit("tableCellDivider"),l=!0,f):(e.enter("data"),c(k)))}function c(k){return k===null||k===124||W(k)?(e.exit("data"),f(k)):(e.consume(k),k===92?p:c)}function p(k){return k===92||k===124?(e.consume(k),c):c(k)}function h(k){return r.interrupt=!1,r.parser.lazy[r.now().line]?n(k):(e.enter("tableDelimiterRow"),l=!1,O(k)?B(e,g,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(k):g(k))}function g(k){return k===45||k===58?S(k):k===124?(l=!0,e.enter("tableCellDivider"),e.consume(k),e.exit("tableCellDivider"),w):_(k)}function w(k){return O(k)?B(e,S,"whitespace")(k):S(k)}function S(k){return k===58?(o+=1,l=!0,e.enter("tableDelimiterMarker"),e.consume(k),e.exit("tableDelimiterMarker"),y):k===45?(o+=1,y(k)):k===null||z(k)?F(k):_(k)}function y(k){return k===45?(e.enter("tableDelimiterFiller"),I(k)):_(k)}function I(k){return k===45?(e.consume(k),I):k===58?(l=!0,e.exit("tableDelimiterFiller"),e.enter("tableDelimiterMarker"),e.consume(k),e.exit("tableDelimiterMarker"),C):(e.exit("tableDelimiterFiller"),C(k))}function C(k){return O(k)?B(e,F,"whitespace")(k):F(k)}function F(k){return k===124?g(k):k===null||z(k)?!l||i!==o?_(k):(e.exit("tableDelimiterRow"),e.exit("tableHead"),t(k)):_(k)}function _(k){return n(k)}function b(k){return e.enter("tableRow"),M(k)}function M(k){return k===124?(e.enter("tableCellDivider"),e.consume(k),e.exit("tableCellDivider"),M):k===null||z(k)?(e.exit("tableRow"),t(k)):O(k)?B(e,M,"whitespace")(k):(e.enter("data"),H(k))}function H(k){return k===null||k===124||W(k)?(e.exit("data"),M(k)):(e.consume(k),k===92?j:H)}function j(k){return k===92||k===124?(e.consume(k),H):H(k)}}function Fc(e,t){let n=-1,r=!0,i=0,o=[0,0,0,0],l=[0,0,0,0],a=!1,u=0,s,f,c;const p=new Pc;for(;++nn[2]+1){const g=n[2]+1,w=n[3]-n[2]-1;e.add(g,w,[])}}e.add(n[3]+1,0,[["exit",c,t]])}return i!==void 0&&(o.end=Object.assign({},Le(t.events,i)),e.add(i,0,[["exit",o,t]]),o=void 0),o}function Zt(e,t,n,r,i){const o=[],l=Le(t.events,n);i&&(i.end=Object.assign({},l),o.push(["exit",i,t])),r.end=Object.assign({},l),o.push(["exit",r,t]),e.add(n+1,0,o)}function Le(e,t){const n=e[t],r=n[0]==="enter"?"start":"end";return n[1][r]}const _c={name:"tasklistCheck",tokenize:Mc};function Oc(){return{text:{91:_c}}}function Mc(e,t,n){const r=this;return i;function i(u){return r.previous!==null||!r._gfmTasklistFirstContentOfListItem?n(u):(e.enter("taskListCheck"),e.enter("taskListCheckMarker"),e.consume(u),e.exit("taskListCheckMarker"),o)}function o(u){return W(u)?(e.enter("taskListCheckValueUnchecked"),e.consume(u),e.exit("taskListCheckValueUnchecked"),l):u===88||u===120?(e.enter("taskListCheckValueChecked"),e.consume(u),e.exit("taskListCheckValueChecked"),l):n(u)}function l(u){return u===93?(e.enter("taskListCheckMarker"),e.consume(u),e.exit("taskListCheckMarker"),e.exit("taskListCheck"),a):n(u)}function a(u){return z(u)?t(u):O(u)?e.check({tokenize:Nc},t,n)(u):n(u)}}function Nc(e,t,n){return B(e,r,"whitespace");function r(i){return i===null?n(i):t(i)}}function Bc(e){return gr([cc(),bc(),vc(e),Lc(),Oc()])}const jc={};function Hc(e){const t=this,n=e||jc,r=t.data(),i=r.micromarkExtensions||(r.micromarkExtensions=[]),o=r.fromMarkdownExtensions||(r.fromMarkdownExtensions=[]),l=r.toMarkdownExtensions||(r.toMarkdownExtensions=[]);i.push(Bc(n)),o.push(oc()),l.push(ac(n))}const Uc=`# Admin dashboard Otari ships with a web admin dashboard for operators. It browses the model catalogue, sets model pricing, manages aliases, adds and edits provider API @@ -173,6 +173,9 @@ gateway. - **Activity**: the per-request log of what the gateway served, with filters. Use it to inspect individual requests, their models, and their outcomes. + Requests the gateway refused are logged too, so filtering to the \`error\` + status shows what is being dropped and why (for example a model with no + pricing under \`require_pricing\`). - **Usage**: aggregate usage and analytics, showing spend and volume over time, broken down by model and by user. diff --git a/src/gateway/static/dashboard/assets/FilterChips-BbDynOdb.js b/src/gateway/static/dashboard/assets/FilterChips-BbDynOdb.js new file mode 100644 index 00000000..0c18f882 --- /dev/null +++ b/src/gateway/static/dashboard/assets/FilterChips-BbDynOdb.js @@ -0,0 +1 @@ +import{j as t}from"./tanstack-query-1t81HyiD.js";import{r as f}from"./react-dgEcD0HR.js";import{B as j,S as Q,$ as Y,h as ee,i as te}from"./heroui-BI50yK5B.js";import{am as re,an as ne,ao as ae,ap as se}from"./index-Br3lxdY5.js";import{R as ie,B as oe,X as le,T as ce,a as de}from"./recharts-C4kRDmvS.js";const ue="var(--otari-brand)",me=10,xe=22;function U(s,d){const l=new Date(s);return Number.isNaN(l.getTime())?s:d==="hour"?l.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit",timeZone:"UTC"}):l.toLocaleDateString(void 0,{month:"short",day:"numeric",timeZone:"UTC"})}function he({active:s,label:d,payload:l,bucket:p}){var u;const g=(u=l==null?void 0:l[0])==null?void 0:u.value;return!s||typeof g!="number"||typeof d!="string"?null:t.jsxs("div",{className:"rounded-md border border-[var(--otari-line)] bg-[var(--otari-surface)] px-2 py-1 text-xs shadow-sm",children:[t.jsxs("div",{className:"text-[var(--otari-muted)]",children:[U(d,p)," · UTC"]}),t.jsxs("div",{className:"font-medium tabular-nums text-[var(--otari-ink)]",children:[g.toLocaleString()," requests"]})]})}const fe="group top-1/2 h-full w-3 cursor-ew-resize outline-none",q=["Window start","Window end"];function E({index:s}){return t.jsxs(te,{index:s,"aria-label":q[s],className:`${fe} pointer-events-auto`,children:[t.jsx("span",{"aria-hidden":!0,className:"absolute inset-y-0 left-1/2 w-0.5 -translate-x-1/2 bg-[var(--otari-brand)]"}),t.jsx("span",{"aria-hidden":!0,className:"absolute left-1/2 top-1/2 h-6 w-2 -translate-x-1/2 -translate-y-1/2 rounded-full border border-[var(--otari-brand)] bg-[var(--otari-surface)] shadow-sm group-focus-visible:ring-2 group-focus-visible:ring-[var(--otari-brand)]"})]})}function Ne({presets:s,extentKey:d,onPreset:l,onSelectRange:p,onSelectFull:g,series:u,bucket:c,windowStart:k,windowEnd:S,loading:F=!1,ariaLabel:W="Request volume over the selected window",action:_}){const I=u.map(e=>e.bucketStart),O=u.map(e=>({x:e.bucketStart,requests:e.requests})),r=u.length,X=re(k,S),$=()=>{if(r===0)return[0,1];const{startIndex:e,endIndex:i}=ae(I,k,S);return[e,i+1]},[C,K]=f.useState($),y=f.useRef(C),v=e=>{y.current=e,K(e)},w=f.useRef(!1);f.useEffect(()=>{w.current||v($())},[k,S,r]);const b=([e,i])=>{if(w.current=!1,r===0)return;const o=Math.max(0,Math.min(r-1,Math.floor(e+1e-6))),n=Math.max(o,Math.min(r-1,Math.ceil(i-1e-6)-1));if(o===0&&n===r-1){g(),v([0,r]);return}const a=se(I,o,n,c);a&&p(a.startIso,a.endIso)},M=s.findIndex(e=>e.key===d),z=M>=0?s[M].seconds:r*ne(c)/1e3,P=M>=0?s[M+1]:s.find(e=>e.seconds===null||z!==null&&e.seconds>z),[m,x]=C,T=m<=.01&&x>=r-.01,B=e=>{const i=Math.max(1,Math.min(r,e)),o=(m+x)/2;let n=o-i/2,a=o+i/2;n<0&&(a-=n,n=0),a>r&&(n-=a-r,a=r);const N=[Math.max(0,n),Math.min(r,a)];v(N),b(N)},Z=()=>{if(T){P&&l(P);return}B((x-m)*2)},H=()=>B((x-m)/2),V=(e,i)=>{const[o,n]=y.current,a=e===0?[Math.max(0,Math.min(n-1,Math.round(o)+i)),n]:[o,Math.min(r,Math.max(o+1,Math.round(n)+i))];v(a),b(a)},G=e=>{var a,N;const i=e.key==="ArrowRight"||e.key==="ArrowUp"?1:e.key==="ArrowLeft"||e.key==="ArrowDown"?-1:0;if(i===0)return;const o=(N=(a=e.target).getAttribute)==null?void 0:N.call(a,"aria-label"),n=q.indexOf(o);n<0||(e.preventDefault(),e.stopPropagation(),V(n,i))},R=f.useRef(null),h=f.useRef(null),J=e=>{if(!h.current||!R.current)return;const i=R.current.getBoundingClientRect().width;if(i<=0)return;const o=(e.clientX-h.current.x)/i*r,n=h.current.sel[1]-h.current.sel[0],a=Math.max(0,Math.min(r-n,h.current.sel[0]+o));v([a,a+n])},A=r?Math.min(m,x)/r*100:0,L=r?Math.max(m,x)/r*100:100,D=!T;return t.jsxs("div",{className:"flex flex-col gap-2",children:[t.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[s.map(e=>t.jsx(j,{size:"sm",variant:d===e.key?"primary":"outline",onPress:()=>l(e),children:e.label},e.key)),t.jsxs("div",{className:"ml-auto flex items-center gap-3",children:[t.jsxs("span",{className:"text-xs text-[var(--otari-muted)]",children:["Showing ",X," · UTC"]}),_]})]}),t.jsxs("div",{className:"rounded-xl border border-[var(--otari-line)] bg-[var(--otari-surface)] p-2",children:[t.jsxs("div",{className:"flex items-center justify-between gap-2 px-1 pb-1",children:[t.jsxs("span",{className:"text-[11px] font-medium uppercase tracking-wide text-[var(--otari-muted)]",children:["Requests / ",c==="hour"?"hour":"day"]}),t.jsxs("div",{className:"flex items-center gap-1.5",children:[t.jsx("span",{className:"hidden text-[11px] text-[var(--otari-muted)] sm:inline",children:"drag the edges to zoom · the bottom strip to pan"}),t.jsx(j,{size:"sm",variant:"ghost",isIconOnly:!0,"aria-label":"Zoom in",isDisabled:r===0,onPress:H,children:t.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-4 w-4","aria-hidden":"true",children:t.jsx("path",{d:"M12 5v14M5 12h14",strokeLinecap:"round"})})}),t.jsx(j,{size:"sm",variant:"ghost",isIconOnly:!0,"aria-label":"Zoom out",isDisabled:r===0||T&&!P,onPress:Z,children:t.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-4 w-4","aria-hidden":"true",children:t.jsx("path",{d:"M5 12h14",strokeLinecap:"round"})})}),D?t.jsx(j,{size:"sm",variant:"ghost",onPress:()=>b([0,r]),children:"Reset"}):null]})]}),F&&r===0?t.jsx("div",{className:"flex h-[90px] items-center justify-center",children:t.jsx(Q,{size:"sm"})}):r===0?t.jsx("div",{className:"flex h-[90px] items-center justify-center text-xs text-[var(--otari-muted)]",children:"No activity in this range."}):t.jsxs("div",{ref:R,className:"relative w-full",children:[t.jsx("div",{role:"img","aria-label":W,className:"w-full touch-pan-y select-none",children:t.jsx(ie,{width:"100%",height:90,children:t.jsxs(oe,{data:O,margin:{top:4,right:0,left:0,bottom:0},children:[t.jsx(le,{dataKey:"x",tickLine:!1,axisLine:!1,interval:"preserveStartEnd",minTickGap:40,tickFormatter:e=>U(e,c),tick:{fontSize:10,fill:"var(--otari-muted)"}}),t.jsx(ce,{cursor:{fill:"var(--otari-line)",opacity:.35},content:t.jsx(he,{bucket:c})}),t.jsx(de,{dataKey:"requests",fill:ue,radius:[2,2,0,0],isAnimationActive:!1})]})})}),D?t.jsxs(t.Fragment,{children:[t.jsx("div",{"aria-hidden":!0,className:"pointer-events-none absolute inset-y-0 left-0 bg-[var(--otari-bg)] opacity-70",style:{width:`${A}%`}}),t.jsx("div",{"aria-hidden":!0,className:"pointer-events-none absolute inset-y-0 right-0 bg-[var(--otari-bg)] opacity-70",style:{width:`${100-L}%`}})]}):null,t.jsx("div",{"aria-hidden":!0,className:"absolute bottom-0 z-[1] cursor-grab touch-none rounded bg-[var(--otari-brand)]/10 hover:bg-[var(--otari-brand)]/20 active:cursor-grabbing",style:{left:`${A}%`,width:`${Math.max(0,L-A)}%`,height:xe},onPointerDown:e=>{e.stopPropagation(),e.preventDefault(),w.current=!0,h.current={x:e.clientX,sel:[Math.min(m,x),Math.max(m,x)]},e.currentTarget.setPointerCapture(e.pointerId)},onPointerMove:J,onPointerUp:e=>{e.currentTarget.releasePointerCapture(e.pointerId),h.current=null,b(y.current)},onPointerCancel:()=>{h.current=null,b(y.current)}}),t.jsx("div",{className:"pointer-events-none absolute inset-0 z-[2]",onKeyDownCapture:G,children:t.jsx(Y,{"aria-label":"Selected time range",minValue:0,maxValue:r,step:1/me,value:C,onChange:e=>{w.current=!0,Array.isArray(e)&&e.length===2&&v([e[0],e[1]])},onChangeEnd:e=>{Array.isArray(e)&&e.length===2&&b([e[0],e[1]])},className:"pointer-events-none h-full w-full",children:t.jsxs(ee,{className:"pointer-events-none relative h-full w-full",children:[t.jsx(E,{index:0}),t.jsx(E,{index:1})]})})})]})]})]})}function ye({chips:s,children:d,onClearAll:l}){const[p,g]=f.useState(!1),u=f.useId();return t.jsxs("div",{className:"flex flex-col gap-2",children:[t.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[t.jsx(j,{size:"sm",variant:"outline",onPress:()=>g(c=>!c),"aria-expanded":p,"aria-controls":u,children:p?"Done":"Add filter"}),s.map(c=>t.jsxs("span",{className:"inline-flex items-center gap-1 rounded-full border border-[var(--otari-line)] bg-[var(--otari-brand-tint)] py-0.5 pl-2.5 pr-1 text-xs text-[var(--otari-brand-dark)]",children:[t.jsxs("span",{className:"text-[var(--otari-muted)]",children:[c.label,":"]}),t.jsx("span",{className:"font-medium",children:c.value}),t.jsx("button",{type:"button",onClick:c.onClear,"aria-label":`Remove ${c.label} filter`,className:"ml-0.5 inline-flex h-4 w-4 items-center justify-center rounded-full text-[var(--otari-muted)] hover:bg-[var(--otari-line)] hover:text-[var(--otari-ink)]",children:t.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",className:"h-3 w-3","aria-hidden":"true",children:t.jsx("path",{d:"M6 6l12 12M18 6L6 18",strokeLinecap:"round"})})})]},c.key)),s.length>0&&l?t.jsx(j,{size:"sm",variant:"ghost",onPress:l,children:"Clear all"}):null]}),t.jsx("div",{id:u,className:p?"flex flex-wrap items-end gap-3":"hidden",children:d})]})}export{Ne as A,ye as F}; diff --git a/src/gateway/static/dashboard/assets/FilterChips-DZjxf-ot.js b/src/gateway/static/dashboard/assets/FilterChips-DZjxf-ot.js deleted file mode 100644 index 02743a60..00000000 --- a/src/gateway/static/dashboard/assets/FilterChips-DZjxf-ot.js +++ /dev/null @@ -1 +0,0 @@ -import{j as t}from"./tanstack-query-1t81HyiD.js";import{r as y}from"./react-dgEcD0HR.js";import{B as T,S as ne,$ as re,h as ae,i as se}from"./heroui-BI50yK5B.js";import{R as ie,B as oe,X as le,T as ce,a as de}from"./recharts-C4kRDmvS.js";const U=3600,g=86400,ue=366,Te=[{key:"1h",label:"Last hour",seconds:U,bucket:"hour"},{key:"24h",label:"24h",seconds:g,bucket:"hour"},{key:"7d",label:"7d",seconds:7*g,bucket:"day"},{key:"30d",label:"30d",seconds:30*g,bucket:"day"},{key:"90d",label:"90d",seconds:90*g,bucket:"day"},{key:"12mo",label:"12mo",seconds:ue*g,bucket:"day"}],Me="30d",Ne=[{key:"1h",label:"1h",seconds:U,bucket:"hour"},{key:"24h",label:"24h",seconds:g,bucket:"hour"},{key:"7d",label:"7d",seconds:7*g,bucket:"day"},{key:"30d",label:"30d",seconds:30*g,bucket:"day"},{key:"all",label:"All",seconds:null,bucket:"day"}],Se="24h",De="custom";function Ae(n,r){return n.find(a=>a.key===r)}function Ce(n,r=Date.now()){return new Date(r-n*1e3).toISOString()}function O(n){return(n==="hour"?U:g)*1e3}function Pe(n,r,a=Date.now()){const o=new Date(n).getTime();return(r?new Date(r).getTime():a)-o<=g*1e3?"hour":"day"}function me(n,r,a,o){if(n.length===0)return null;const h=Math.max(0,Math.min(r,a)),l=Math.min(n.length-1,Math.max(r,a)),i=new Date(n[h]).getTime(),f=new Date(n[l]).getTime()+O(o);return{startIso:new Date(i).toISOString(),endIso:new Date(f).toISOString()}}function he(n,r,a){const o=n.length;if(o===0)return{startIndex:0,endIndex:0};const h=n.map(f=>new Date(f).getTime());let l=0;if(r){const f=new Date(r).getTime();for(let x=0;xe.bucketStart),Z=l.map(e=>({x:e.bucketStart,requests:e.requests})),s=l.length,H=xe(f,x),I=()=>{if(s===0)return[0,1];const{startIndex:e,endIndex:u}=he($,f,x);return[e,u+1]},[A,V]=y.useState(I),N=y.useRef(A),w=e=>{N.current=e,V(e)},S=y.useRef(!1);y.useEffect(()=>{S.current||w(I())},[f,x,s]);const k=([e,u])=>{if(S.current=!1,s===0)return;const m=Math.max(0,Math.min(s-1,Math.floor(e+1e-6))),c=Math.max(m,Math.min(s-1,Math.ceil(u-1e-6)-1));if(m===0&&c===s-1){h(),w([0,s]);return}const d=me($,m,c,i);d&&o(d.startIso,d.endIso)},D=n.findIndex(e=>e.key===r),L=D>=0?n[D].seconds:s*O(i)/1e3,C=D>=0?n[D+1]:n.find(e=>e.seconds===null||L!==null&&e.seconds>L),[p,b]=A,P=p<=.01&&b>=s-.01,_=e=>{const u=Math.max(1,Math.min(s,e)),m=(p+b)/2;let c=m-u/2,d=m+u/2;c<0&&(d-=c,c=0),d>s&&(c-=d-s,d=s);const M=[Math.max(0,c),Math.min(s,d)];w(M),k(M)},G=()=>{if(P){C&&a(C);return}_((b-p)*2)},J=()=>_((b-p)/2),Q=(e,u)=>{const[m,c]=N.current,d=e===0?[Math.max(0,Math.min(c-1,Math.round(m)+u)),c]:[m,Math.min(s,Math.max(m+1,Math.round(c)+u))];w(d),k(d)},ee=e=>{var d,M;const u=e.key==="ArrowRight"||e.key==="ArrowUp"?1:e.key==="ArrowLeft"||e.key==="ArrowDown"?-1:0;if(u===0)return;const m=(M=(d=e.target).getAttribute)==null?void 0:M.call(d,"aria-label"),c=K.indexOf(m);c<0||(e.preventDefault(),e.stopPropagation(),Q(c,u))},R=y.useRef(null),v=y.useRef(null),te=e=>{if(!v.current||!R.current)return;const u=R.current.getBoundingClientRect().width;if(u<=0)return;const m=(e.clientX-v.current.x)/u*s,c=v.current.sel[1]-v.current.sel[0],d=Math.max(0,Math.min(s-c,v.current.sel[0]+m));w([d,d+c])},E=s?Math.min(p,b)/s*100:0,z=s?Math.max(p,b)/s*100:100,B=!P;return t.jsxs("div",{className:"flex flex-col gap-2",children:[t.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[n.map(e=>t.jsx(T,{size:"sm",variant:r===e.key?"primary":"outline",onPress:()=>a(e),children:e.label},e.key)),t.jsxs("div",{className:"ml-auto flex items-center gap-3",children:[t.jsxs("span",{className:"text-xs text-[var(--otari-muted)]",children:["Showing ",H," · UTC"]}),X]})]}),t.jsxs("div",{className:"rounded-xl border border-[var(--otari-line)] bg-[var(--otari-surface)] p-2",children:[t.jsxs("div",{className:"flex items-center justify-between gap-2 px-1 pb-1",children:[t.jsxs("span",{className:"text-[11px] font-medium uppercase tracking-wide text-[var(--otari-muted)]",children:["Requests / ",i==="hour"?"hour":"day"]}),t.jsxs("div",{className:"flex items-center gap-1.5",children:[t.jsx("span",{className:"hidden text-[11px] text-[var(--otari-muted)] sm:inline",children:"drag the edges to zoom · the bottom strip to pan"}),t.jsx(T,{size:"sm",variant:"ghost",isIconOnly:!0,"aria-label":"Zoom in",isDisabled:s===0,onPress:J,children:t.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-4 w-4","aria-hidden":"true",children:t.jsx("path",{d:"M12 5v14M5 12h14",strokeLinecap:"round"})})}),t.jsx(T,{size:"sm",variant:"ghost",isIconOnly:!0,"aria-label":"Zoom out",isDisabled:s===0||P&&!C,onPress:G,children:t.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-4 w-4","aria-hidden":"true",children:t.jsx("path",{d:"M5 12h14",strokeLinecap:"round"})})}),B?t.jsx(T,{size:"sm",variant:"ghost",onPress:()=>k([0,s]),children:"Reset"}):null]})]}),W&&s===0?t.jsx("div",{className:"flex h-[90px] items-center justify-center",children:t.jsx(ne,{size:"sm"})}):s===0?t.jsx("div",{className:"flex h-[90px] items-center justify-center text-xs text-[var(--otari-muted)]",children:"No activity in this range."}):t.jsxs("div",{ref:R,className:"relative w-full",children:[t.jsx("div",{role:"img","aria-label":q,className:"w-full touch-pan-y select-none",children:t.jsx(ie,{width:"100%",height:90,children:t.jsxs(oe,{data:Z,margin:{top:4,right:0,left:0,bottom:0},children:[t.jsx(le,{dataKey:"x",tickLine:!1,axisLine:!1,interval:"preserveStartEnd",minTickGap:40,tickFormatter:e=>Y(e,i),tick:{fontSize:10,fill:"var(--otari-muted)"}}),t.jsx(ce,{cursor:{fill:"var(--otari-line)",opacity:.35},content:t.jsx(be,{bucket:i})}),t.jsx(de,{dataKey:"requests",fill:fe,radius:[2,2,0,0],isAnimationActive:!1})]})})}),B?t.jsxs(t.Fragment,{children:[t.jsx("div",{"aria-hidden":!0,className:"pointer-events-none absolute inset-y-0 left-0 bg-[var(--otari-bg)] opacity-70",style:{width:`${E}%`}}),t.jsx("div",{"aria-hidden":!0,className:"pointer-events-none absolute inset-y-0 right-0 bg-[var(--otari-bg)] opacity-70",style:{width:`${100-z}%`}})]}):null,t.jsx("div",{"aria-hidden":!0,className:"absolute bottom-0 z-[1] cursor-grab touch-none rounded bg-[var(--otari-brand)]/10 hover:bg-[var(--otari-brand)]/20 active:cursor-grabbing",style:{left:`${E}%`,width:`${Math.max(0,z-E)}%`,height:pe},onPointerDown:e=>{e.stopPropagation(),e.preventDefault(),S.current=!0,v.current={x:e.clientX,sel:[Math.min(p,b),Math.max(p,b)]},e.currentTarget.setPointerCapture(e.pointerId)},onPointerMove:te,onPointerUp:e=>{e.currentTarget.releasePointerCapture(e.pointerId),v.current=null,k(N.current)},onPointerCancel:()=>{v.current=null,k(N.current)}}),t.jsx("div",{className:"pointer-events-none absolute inset-0 z-[2]",onKeyDownCapture:ee,children:t.jsx(re,{"aria-label":"Selected time range",minValue:0,maxValue:s,step:1/ge,value:A,onChange:e=>{S.current=!0,Array.isArray(e)&&e.length===2&&w([e[0],e[1]])},onChangeEnd:e=>{Array.isArray(e)&&e.length===2&&k([e[0],e[1]])},className:"pointer-events-none h-full w-full",children:t.jsxs(ae,{className:"pointer-events-none relative h-full w-full",children:[t.jsx(F,{index:0}),t.jsx(F,{index:1})]})})})]})]})]})}function Ee({chips:n,children:r,onClearAll:a}){const[o,h]=y.useState(!1),l=y.useId();return t.jsxs("div",{className:"flex flex-col gap-2",children:[t.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[t.jsx(T,{size:"sm",variant:"outline",onPress:()=>h(i=>!i),"aria-expanded":o,"aria-controls":l,children:o?"Done":"Add filter"}),n.map(i=>t.jsxs("span",{className:"inline-flex items-center gap-1 rounded-full border border-[var(--otari-line)] bg-[var(--otari-brand-tint)] py-0.5 pl-2.5 pr-1 text-xs text-[var(--otari-brand-dark)]",children:[t.jsxs("span",{className:"text-[var(--otari-muted)]",children:[i.label,":"]}),t.jsx("span",{className:"font-medium",children:i.value}),t.jsx("button",{type:"button",onClick:i.onClear,"aria-label":`Remove ${i.label} filter`,className:"ml-0.5 inline-flex h-4 w-4 items-center justify-center rounded-full text-[var(--otari-muted)] hover:bg-[var(--otari-line)] hover:text-[var(--otari-ink)]",children:t.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",className:"h-3 w-3","aria-hidden":"true",children:t.jsx("path",{d:"M6 6l12 12M18 6L6 18",strokeLinecap:"round"})})})]},i.key)),n.length>0&&a?t.jsx(T,{size:"sm",variant:"ghost",onPress:a,children:"Clear all"}):null]}),t.jsx("div",{id:l,className:o?"flex flex-wrap items-end gap-3":"hidden",children:r})]})}export{Re as A,De as C,Ee as F,Me as U,Ne as a,Pe as b,Se as c,Te as d,Ae as f,Ce as i}; diff --git a/src/gateway/static/dashboard/assets/KeysPage-CZjqxzex.js b/src/gateway/static/dashboard/assets/KeysPage--hq_RPOL.js similarity index 85% rename from src/gateway/static/dashboard/assets/KeysPage-CZjqxzex.js rename to src/gateway/static/dashboard/assets/KeysPage--hq_RPOL.js index e46c62e1..e8c3892f 100644 --- a/src/gateway/static/dashboard/assets/KeysPage-CZjqxzex.js +++ b/src/gateway/static/dashboard/assets/KeysPage--hq_RPOL.js @@ -1,4 +1,4 @@ -import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as l}from"./react-dgEcD0HR.js";import{a as Q,s as q,t as X,v as Z,P as ee,E as O,q as te,w as se,u as U,I as ae}from"./index-DV8qvdUj.js";import{u as ne,r as re,B as ie}from"./tableSelection-9hV37uhu.js";import{C as le}from"./ConfirmDialog-KvA7b1-a.js";import{D as oe}from"./DataTable-Bmn_eYSV.js";import{F as D}from"./Field-Dwp6u3t8.js";import{a as F,M as W}from"./ModelScopeControl-u-XlXgXz.js";import{C as P,L as ce,I as de,a as ue,b as me,D as xe,f as A,B as g,d as I}from"./heroui-BI50yK5B.js";function he({value:t,onChange:a,users:n,description:i}){const u=n.filter(r=>!r.user_id.startsWith("apikey-")).map(r=>({id:r.user_id,name:r.alias?`${r.user_id} (${r.alias})`:r.user_id})),[o,c]=l.useState(t),x=o.trim().toLowerCase(),y=u.filter(r=>!x||r.id.toLowerCase().includes(x)||r.name.toLowerCase().includes(x)).slice(0,50),p=r=>{const m=r.trim(),j=u.find(v=>v.id===m||v.name===m);return j?j.id:m},d=p(o),h=u.some(r=>r.id===d),f=d!==""&&!h?e.jsxs("span",{children:["Creates a new user ",e.jsx("code",{children:d}),"."]}):i??"Spend and budgets track against this user.";return e.jsxs(P.Root,{allowsCustomValue:!0,allowsEmptyCollection:!0,menuTrigger:"focus",inputValue:o,onInputChange:r=>{c(r),a(p(r))},onSelectionChange:r=>{if(r!=null){const m=String(r);c(m),a(m)}},className:"flex max-w-md flex-col gap-1",children:[e.jsx(ce,{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Owner"}),e.jsxs(P.InputGroup,{children:[e.jsx(de,{placeholder:"Pick a user, or type a new id…",autoComplete:"off","data-1p-ignore":!0,"data-lpignore":"true",onFocus:r=>r.currentTarget.select()}),e.jsx(P.Trigger,{})]}),e.jsx(P.Popover,{children:e.jsx(ue,{items:y,className:"max-h-72 overflow-auto",children:r=>e.jsx(me,{id:r.id,textValue:r.name,children:r.name})})}),e.jsx(xe,{className:"text-xs text-[var(--otari-muted)]",children:f})]})}function H(t){if(!t)return"—";const a=new Date(t);return Number.isNaN(a.getTime())?"—":a.toLocaleDateString()}function fe(t){if(!t)return null;const a=new Date(t).getTime();if(Number.isNaN(a))return null;const n=Math.round((a-Date.now())/1e3),i=Math.abs(n),u=[["day",86400],["hour",3600],["minute",60]],o=new Intl.RelativeTimeFormat(void 0,{numeric:"auto"});for(const[c,x]of u)if(i>=x)return o.format(Math.round(n/x),c);return o.format(n,"second")}function pe(t){if(!t.expires_at)return!1;const a=new Date(t.expires_at).getTime();return!Number.isNaN(a)&&aString(i).padStart(2,"0");return`${a.getFullYear()}-${n(a.getMonth()+1)}-${n(a.getDate())}T${n(a.getHours())}:${n(a.getMinutes())}`}const ye=t=>(t??"").startsWith("apikey-"),M=t=>t.key_name??t.id,ve=t=>t.id;function K({label:t,value:a,multiline:n=!1,fieldRef:i}){const u=l.useRef(null),o=i??u,[c,x]=l.useState(!1),[y,p]=l.useState(!1),d=async()=>{var f,r,m;(f=o.current)==null||f.focus(),(r=o.current)==null||r.select();try{if((m=navigator.clipboard)!=null&&m.writeText){await navigator.clipboard.writeText(a),x(!0),p(!1),window.setTimeout(()=>x(!1),2e3);return}}catch{}p(!0)},h="w-full rounded-lg border border-[var(--otari-line)] bg-[var(--otari-bg)] px-3 py-2 font-mono text-xs text-[var(--otari-ink)]";return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-xs font-medium text-[var(--otari-muted)]",children:t}),e.jsx(g,{size:"sm",variant:"outline",onPress:d,children:c?"Copied":"Copy"})]}),n?e.jsx("textarea",{ref:o,readOnly:!0,rows:a.split(` -`).length,value:a,onFocus:f=>f.currentTarget.select(),className:`${h} resize-none whitespace-pre`}):e.jsx("input",{ref:o,readOnly:!0,value:a,onFocus:f=>f.currentTarget.select(),className:h}),e.jsx("span",{"aria-live":"polite",className:"text-xs text-green-700",children:c?"Copied to clipboard.":""}),y?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Selected. Press Ctrl/Cmd-C to copy."}):null]})}function je({title:t,result:a,onClose:n}){const i=l.useRef(null),u=l.useRef(null),o=typeof window<"u"?window.location.origin:"",c=a.key;l.useEffect(()=>{var d,h;(d=u.current)==null||d.focus(),(h=u.current)==null||h.select()},[]);const x=d=>{var m;if(d.key!=="Tab")return;const h=(m=i.current)==null?void 0:m.querySelectorAll('button, input, textarea, a[href], [tabindex]:not([tabindex="-1"])');if(!h||h.length===0)return;const f=h[0],r=h[h.length-1];d.shiftKey&&document.activeElement===f?(d.preventDefault(),r.focus()):!d.shiftKey&&document.activeElement===r&&(d.preventDefault(),f.focus())},y=[`curl ${o}/v1/chat/completions \\`,` -H "Otari-Key: ${c}" \\`,' -H "Content-Type: application/json" \\',` -d '{"model": "your-model", "messages": [{"role": "user", "content": "Hello"}]}'`].join(` +import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as l}from"./react-dgEcD0HR.js";import{a as Q,y as q,z as X,B as Z,P as ee,E as O,w as te,D as se,u as U,I as ae}from"./index-Br3lxdY5.js";import{u as ne,r as re,B as ie}from"./tableSelection-9hV37uhu.js";import{C as le}from"./ConfirmDialog-KQrh5qGO.js";import{D as oe}from"./DataTable-Bmn_eYSV.js";import{F as A}from"./Field-Dwp6u3t8.js";import{a as F,M as W}from"./ModelScopeControl-DYPdGezt.js";import{C as P,L as ce,I as de,a as ue,b as me,D as xe,f as D,B as g,d as I}from"./heroui-BI50yK5B.js";function he({value:t,onChange:a,users:n,description:i}){const u=n.filter(r=>!r.user_id.startsWith("apikey-")).map(r=>({id:r.user_id,name:r.alias?`${r.user_id} (${r.alias})`:r.user_id})),[o,c]=l.useState(t),x=o.trim().toLowerCase(),y=u.filter(r=>!x||r.id.toLowerCase().includes(x)||r.name.toLowerCase().includes(x)).slice(0,50),p=r=>{const m=r.trim(),v=u.find(j=>j.id===m||j.name===m);return v?v.id:m},d=p(o),h=u.some(r=>r.id===d),f=d!==""&&!h?e.jsxs("span",{children:["Creates a new user ",e.jsx("code",{children:d}),"."]}):i??"Spend and budgets track against this user.";return e.jsxs(P.Root,{allowsCustomValue:!0,allowsEmptyCollection:!0,menuTrigger:"focus",inputValue:o,onInputChange:r=>{c(r),a(p(r))},onSelectionChange:r=>{if(r!=null){const m=String(r);c(m),a(m)}},className:"flex max-w-md flex-col gap-1",children:[e.jsx(ce,{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Owner"}),e.jsxs(P.InputGroup,{children:[e.jsx(de,{placeholder:"Pick a user, or type a new id…",autoComplete:"off","data-1p-ignore":!0,"data-lpignore":"true",onFocus:r=>r.currentTarget.select()}),e.jsx(P.Trigger,{})]}),e.jsx(P.Popover,{children:e.jsx(ue,{items:y,className:"max-h-72 overflow-auto",children:r=>e.jsx(me,{id:r.id,textValue:r.name,children:r.name})})}),e.jsx(xe,{className:"text-xs text-[var(--otari-muted)]",children:f})]})}function H(t){if(!t)return"—";const a=new Date(t);return Number.isNaN(a.getTime())?"—":a.toLocaleDateString()}function fe(t){if(!t)return null;const a=new Date(t).getTime();if(Number.isNaN(a))return null;const n=Math.round((a-Date.now())/1e3),i=Math.abs(n),u=[["day",86400],["hour",3600],["minute",60]],o=new Intl.RelativeTimeFormat(void 0,{numeric:"auto"});for(const[c,x]of u)if(i>=x)return o.format(Math.round(n/x),c);return o.format(n,"second")}function pe(t){if(!t.expires_at)return!1;const a=new Date(t.expires_at).getTime();return!Number.isNaN(a)&&aString(i).padStart(2,"0");return`${a.getFullYear()}-${n(a.getMonth()+1)}-${n(a.getDate())}T${n(a.getHours())}:${n(a.getMinutes())}`}const ye=t=>(t??"").startsWith("apikey-"),M=t=>t.key_name??t.id,je=t=>t.id;function K({label:t,value:a,multiline:n=!1,fieldRef:i}){const u=l.useRef(null),o=i??u,[c,x]=l.useState(!1),[y,p]=l.useState(!1),d=async()=>{var f,r,m;(f=o.current)==null||f.focus(),(r=o.current)==null||r.select();try{if((m=navigator.clipboard)!=null&&m.writeText){await navigator.clipboard.writeText(a),x(!0),p(!1),window.setTimeout(()=>x(!1),2e3);return}}catch{}p(!0)},h="w-full rounded-lg border border-[var(--otari-line)] bg-[var(--otari-bg)] px-3 py-2 font-mono text-xs text-[var(--otari-ink)]";return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-xs font-medium text-[var(--otari-muted)]",children:t}),e.jsx(g,{size:"sm",variant:"outline",onPress:d,children:c?"Copied":"Copy"})]}),n?e.jsx("textarea",{ref:o,readOnly:!0,rows:a.split(` +`).length,value:a,onFocus:f=>f.currentTarget.select(),className:`${h} resize-none whitespace-pre`}):e.jsx("input",{ref:o,readOnly:!0,value:a,onFocus:f=>f.currentTarget.select(),className:h}),e.jsx("span",{"aria-live":"polite",className:"text-xs text-green-700",children:c?"Copied to clipboard.":""}),y?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Selected. Press Ctrl/Cmd-C to copy."}):null]})}function ve({title:t,result:a,onClose:n}){const i=l.useRef(null),u=l.useRef(null),o=typeof window<"u"?window.location.origin:"",c=a.key;l.useEffect(()=>{var d,h;(d=u.current)==null||d.focus(),(h=u.current)==null||h.select()},[]);const x=d=>{var m;if(d.key!=="Tab")return;const h=(m=i.current)==null?void 0:m.querySelectorAll('button, input, textarea, a[href], [tabindex]:not([tabindex="-1"])');if(!h||h.length===0)return;const f=h[0],r=h[h.length-1];d.shiftKey&&document.activeElement===f?(d.preventDefault(),r.focus()):!d.shiftKey&&document.activeElement===r&&(d.preventDefault(),f.focus())},y=[`curl ${o}/v1/chat/completions \\`,` -H "Otari-Key: ${c}" \\`,' -H "Content-Type: application/json" \\',` -d '{"model": "your-model", "messages": [{"role": "user", "content": "Hello"}]}'`].join(` `),p=["from openai import OpenAI","",`client = OpenAI(base_url="${o}/v1", api_key="${c}")`,"resp = client.chat.completions.create(",' model="your-model",',' messages=[{"role": "user", "content": "Hello"}],',")","print(resp.choices[0].message.content)"].join(` -`);return e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",role:"presentation",children:e.jsxs("div",{ref:i,role:"dialog","aria-modal":"true","aria-labelledby":"reveal-title",onKeyDown:x,className:"flex max-h-[90vh] w-full max-w-2xl flex-col gap-4 overflow-y-auto rounded-xl bg-[var(--otari-surface)] p-6 shadow-xl",children:[e.jsx("h2",{id:"reveal-title",className:"text-lg font-semibold text-[var(--otari-ink)]",children:t}),e.jsx(ae,{tone:"warning",children:"Copy this key now. For security it is shown only once and cannot be retrieved later. If you lose it, use Regenerate to issue a new secret."}),e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["Model access: ",F(a.allowed_models).text,"."]}),e.jsx(K,{label:"Secret key",value:c,fieldRef:u}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Make your first call"}),e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["Replace ",e.jsx("code",{children:"your-model"})," with a model from the Models page."]})]}),e.jsx(K,{label:"curl",value:y,multiline:!0}),e.jsx(K,{label:"Python (OpenAI SDK)",value:p,multiline:!0})]}),e.jsx("div",{className:"flex justify-end",children:e.jsx(g,{variant:"primary",onPress:n,children:"I’ve saved this key"})})]})})}function V({trigger:t,message:a,confirmLabel:n,isPending:i,onConfirm:u}){const[o,c]=l.useState(!1);return o?e.jsxs("div",{className:"flex flex-col items-end gap-1.5 rounded-lg border border-amber-200 bg-amber-50 p-2 text-right",children:[e.jsx("span",{className:"max-w-xs text-xs text-amber-800",children:a}),e.jsxs("span",{className:"inline-flex gap-1",children:[e.jsx(g,{size:"sm",variant:"danger",isDisabled:i,onPress:u,children:n}),e.jsx(g,{size:"sm",variant:"ghost",isDisabled:i,onPress:()=>c(!1),children:"Cancel"})]})]}):e.jsx(g,{size:"sm",variant:"danger-soft",onPress:()=>c(!0),children:t})}function G({userId:t,users:a}){const n=t.trim();if(n==="")return e.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:"Choose an owner above to see the models this key can inherit."});const i=a.find(c=>c.user_id===n);if(!i)return e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["New user ",e.jsx("code",{children:n})," starts unrestricted, so this key may allow any model."]});const{text:u}=F(i.allowed_models),o=i.allowed_models&&i.allowed_models.length>0?i.allowed_models.join(", "):null;return e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["Owner ",e.jsx("code",{children:n})," allows ",e.jsx("span",{className:"font-medium text-[var(--otari-ink)]",children:u.toLowerCase()}),o?e.jsxs(e.Fragment,{children:[" (",e.jsx("span",{className:"font-mono",children:o}),")"]}):null,". This key inherits that, or narrows within it."]})}function Y({checked:t,onChange:a}){return e.jsxs("label",{className:"flex items-start gap-2 rounded-lg border border-[var(--otari-line)] p-3 text-sm",children:[e.jsx("input",{type:"checkbox",checked:t,onChange:n=>a(n.target.checked),className:"mt-0.5 h-4 w-4 accent-[var(--otari-brand)]","aria-label":"Exempt this key from budget"}),e.jsxs("span",{className:"flex flex-col gap-0.5",children:[e.jsx("span",{className:"font-medium text-[var(--otari-ink)]",children:"Exempt from budget"}),e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Requests on this key are logged with their cost but never counted toward the owner's budget or spend, and never blocked by it."})]})]})}function be({onClose:t,onCreated:a}){const n=se(),i=U(),[u,o]=l.useState(""),[c,x]=l.useState(""),[y,p]=l.useState(!1),[d,h]=l.useState(""),[f,r]=l.useState(null),[m,j]=l.useState(!1),[v,E]=l.useState(!0),C=c!==""&&new Date(c).getTime(){if(n.isPending||!v||N)return;const b={key_name:u.trim()||null,user_id:d.trim(),expires_at:c?new Date(c).toISOString():null,allowed_models:f,exclude_from_budget:m};n.mutate(b,{onSuccess:w=>{a(w),t()}})};return e.jsx(I,{children:e.jsxs(I.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsx("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Create API key"}),e.jsx(O,{error:n.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(D,{label:"Name",value:u,onChange:o,placeholder:"ci-bot",autoFocus:!0,description:"A label to recognize this key later."}),e.jsx(D,{label:"Expires (optional)",value:c,onChange:x,type:"datetime-local",description:C?e.jsx("span",{className:"text-red-700",children:"That time is in the past; the key would be rejected immediately."}):"Leave blank for a key that never expires."})]}),e.jsx(he,{value:d,onChange:h,users:i.data??[]}),e.jsx("button",{type:"button",className:"self-start text-xs font-medium text-[var(--otari-brand-dark)]",onClick:()=>p(b=>!b),children:y?"Hide advanced":"Advanced"}),y?e.jsxs("div",{className:"flex flex-col gap-4 rounded-lg border border-[var(--otari-line)] p-4",children:[e.jsx(G,{userId:d,users:i.data??[]}),e.jsx(W,{title:"Restrict this key's models",description:"By default this key inherits its owner's access. Optionally narrow it to a subset; a key can never exceed its owner's allowed models.",anyLabel:"Inherit owner access",initial:null,onChange:(b,w)=>{r(b),E(w)}}),e.jsx(Y,{checked:m,onChange:j})]}):null,e.jsxs("div",{className:"flex gap-2",children:[e.jsx(g,{variant:"primary",isDisabled:n.isPending||!v||N,onPress:S,children:n.isPending?"Creating…":"Create key"}),e.jsx(g,{variant:"ghost",onPress:t,children:"Cancel"})]})]})})}function we({apiKey:t,onClose:a}){const n=q(),i=U(),[u,o]=l.useState(t.key_name??""),[c,x]=l.useState(ge(t.expires_at)),[y,p]=l.useState(t.allowed_models),[d,h]=l.useState(t.exclude_from_budget),[f,r]=l.useState(!0),m=()=>{n.isPending||!f||n.mutate({id:t.id,body:{key_name:u.trim()||null,expires_at:c?new Date(c).toISOString():null,allowed_models:y,exclude_from_budget:d}},{onSuccess:a})};return e.jsx(I,{children:e.jsxs(I.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsxs("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:["Edit ",e.jsx("code",{children:t.key_name??t.id})]}),e.jsx(O,{error:n.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(D,{label:"Name",value:u,onChange:o,placeholder:"ci-bot"}),e.jsx(D,{label:"Expires",value:c,onChange:x,type:"datetime-local",description:"Blank clears the expiry."})]}),t.user_id?e.jsx(G,{userId:t.user_id,users:i.data??[]}):null,e.jsx(W,{title:"Restrict this key's models",description:"This key inherits its owner's access by default. Narrow it to a subset here; it can never exceed the owner's allowed models.",anyLabel:"Inherit owner access",initial:t.allowed_models,onChange:(j,v)=>{p(j),r(v)}}),e.jsx(Y,{checked:d,onChange:h}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(g,{variant:"primary",isDisabled:n.isPending||!f,onPress:m,children:n.isPending?"Saving…":"Save changes"}),e.jsx(g,{variant:"ghost",onPress:a,children:"Cancel"})]})]})})}function Ne({apiKey:t}){return t.is_active?pe(t)?e.jsx(A,{size:"sm",color:"warning",children:"Expired"}):e.jsx(A,{size:"sm",color:"accent",children:"Active"}):e.jsx(A,{size:"sm",color:"default",children:"Disabled"})}function ke({allowed:t}){const{text:a,tone:n}=F(t),i=n==="danger"?"text-red-700 font-medium":n==="muted"?"text-[var(--otari-muted)]":"text-[var(--otari-brand-dark)] font-medium",u=t&&t.length>0?t.join(", "):void 0;return e.jsx("span",{className:`text-xs ${i}`,title:u,children:a})}function Re(){const t=Q(),a=q(),n=X(),i=Z(),[u,o]=l.useState(!1),[c,x]=l.useState(null),[y,p]=l.useState(null),d=t.data??[],h=t.isLoading,f=d.find(s=>s.id===c)??null,r=!h&&d.length===0&&!u,m=ne(),[j,v]=l.useState(!1),[E,C]=l.useState(void 0),[N,S]=l.useState(!1),b=d.map(s=>s.id),w=re(m.selectedKeys,b),T=d.filter(s=>w.includes(s.id)),$=l.useCallback((s,k)=>a.mutate({id:s.id,body:{is_active:k}}),[a.mutate]),z=l.useCallback(s=>n.mutate(s.id,{onSuccess:k=>p({title:`New secret for ${M(s)}`,result:k})}),[n.mutate]),R=async(s,k,B)=>{S(!0),C(void 0);try{for(const L of s)await k(L);m.clear(),B==null||B()}catch(L){C(L)}finally{S(!1)}},J=l.useMemo(()=>[{id:"name",header:"Name",isRowHeader:!0,cell:s=>e.jsxs("div",{className:"flex flex-col gap-0.5",children:[e.jsx("span",{className:"font-medium text-[var(--otari-ink)]",children:s.key_name??e.jsx("span",{className:"text-[var(--otari-muted)]",children:"(unnamed)"})}),e.jsxs("div",{className:"flex flex-wrap items-center gap-1",children:[e.jsx(ke,{allowed:s.allowed_models}),s.exclude_from_budget?e.jsx("span",{className:"inline-flex items-center rounded-full border border-[var(--otari-line)] bg-[var(--otari-brand-tint)] px-2 py-0.5 text-xs font-medium text-[var(--otari-brand-dark)]",title:"Requests on this key are logged with cost but never counted toward budget",children:"Budget-exempt"}):null]})]})},{id:"status",header:"Status",cell:s=>e.jsx(Ne,{apiKey:s})},{id:"owner",header:"Owner",cell:s=>ye(s.user_id)?e.jsx(A,{size:"sm",color:"default",children:"virtual"}):e.jsx("code",{className:"text-xs text-[var(--otari-muted)]",children:s.user_id??"—"})},{id:"key",header:"Key",cell:s=>e.jsx("code",{className:"text-xs text-[var(--otari-muted)]",children:s.key_prefix?`${s.key_prefix}…`:"—"})},{id:"created",header:"Created",cell:s=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:H(s.created_at)})},{id:"last_used",header:"Last used",cell:s=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:fe(s.last_used_at)??"never"})},{id:"expires",header:"Expires",cell:s=>e.jsx("span",{className:"text-[var(--otari-muted)]",title:s.expires_at?new Date(s.expires_at).toLocaleString():void 0,children:s.expires_at?H(s.expires_at):"never"})},{id:"actions",header:"Actions",align:"end",cell:s=>e.jsxs("div",{className:"flex items-center justify-end gap-1.5",children:[e.jsx(g,{size:"sm",variant:"outline",isDisabled:a.isPending,onPress:()=>$(s,!s.is_active),children:s.is_active?"Disable":"Enable"}),e.jsx(g,{size:"sm",variant:"ghost",onPress:()=>{o(!1),x(s.id)},children:"Edit"}),e.jsx(V,{trigger:"Regenerate",confirmLabel:"Regenerate",isPending:n.isPending,message:e.jsxs(e.Fragment,{children:["Regenerate the secret for ",e.jsx("strong",{children:M(s)}),"? The current secret stops working immediately, with no grace period."]}),onConfirm:()=>z(s)}),s.is_active?null:e.jsx(V,{trigger:"Delete",confirmLabel:"Delete permanently",isPending:i.isPending,message:e.jsxs(e.Fragment,{children:["Permanently delete ",e.jsx("strong",{children:M(s)}),"? This removes the key and unlinks its usage history. Cannot be undone."]}),onConfirm:()=>i.mutate(s.id)})]})}],[a.isPending,n.isPending,i.isPending,i.mutate,$,z]),_=T.filter(s=>!s.is_active);return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(ee,{title:"API keys",description:"Issue and revoke the keys that authenticate callers to this gateway. Secrets are shown once at creation.",action:u?null:e.jsx(g,{variant:"primary",onPress:()=>{x(null),o(!0)},children:"Create key"})}),e.jsx(O,{error:t.error??a.error??n.error??i.error}),r?e.jsx(te,{title:"No API keys yet",description:"An API key authenticates callers to this gateway. Create one to make your first request; the secret is shown once, so keep it somewhere safe.",actionLabel:"Create your first key",onAction:()=>{x(null),o(!0)}}):null,u?e.jsx(be,{onClose:()=>o(!1),onCreated:s=>p({title:"API key created",result:s})}):null,f?e.jsx(we,{apiKey:f,onClose:()=>x(null)},f.id):null,w.length>0?e.jsxs(ie,{selectedCount:w.length,allMatching:!1,matchingTotal:null,canSelectAllMatching:!1,onSelectAllMatching:()=>{},onClear:m.clear,children:[e.jsx(g,{size:"sm",variant:"outline",isDisabled:N,onPress:()=>void R(T,s=>a.mutateAsync({id:s.id,body:{is_active:!1}})),children:"Disable"}),e.jsx(g,{size:"sm",variant:"outline",isDisabled:N,onPress:()=>void R(T,s=>a.mutateAsync({id:s.id,body:{exclude_from_budget:!0}})),children:"Budget-exempt"}),e.jsx(g,{size:"sm",variant:"danger",isDisabled:_.length===0,onPress:()=>v(!0),children:"Delete"})]}):null,r?null:e.jsx(oe,{ariaLabel:"API keys",columns:J,rows:d,getRowKey:ve,isLoading:h,emptyContent:"No API keys yet. Create one to authenticate a caller.",selectionMode:"multiple",selectedKeys:m.selectedKeys,onSelectionChange:m.onSelectionChange}),e.jsx(le,{isOpen:j,onOpenChange:v,heading:"Delete API keys",body:`Permanently delete ${_.length} disabled ${_.length===1?"key":"keys"}? This removes them and unlinks their usage history. Cannot be undone. Active keys in the selection are skipped; disable them first.`,confirmLabel:"Delete permanently",isPending:N,error:E,onConfirm:()=>void R(_,s=>i.mutateAsync(s.id),()=>v(!1))}),y?e.jsx(je,{title:y.title,result:y.result,onClose:()=>{p(null),n.reset()}}):null]})}export{Re as KeysPage}; +`);return e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",role:"presentation",children:e.jsxs("div",{ref:i,role:"dialog","aria-modal":"true","aria-labelledby":"reveal-title",onKeyDown:x,className:"flex max-h-[90vh] w-full max-w-2xl flex-col gap-4 overflow-y-auto rounded-xl bg-[var(--otari-surface)] p-6 shadow-xl",children:[e.jsx("h2",{id:"reveal-title",className:"text-lg font-semibold text-[var(--otari-ink)]",children:t}),e.jsx(ae,{tone:"warning",children:"Copy this key now. For security it is shown only once and cannot be retrieved later. If you lose it, use Regenerate to issue a new secret."}),e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["Model access: ",F(a.allowed_models).text,"."]}),e.jsx(K,{label:"Secret key",value:c,fieldRef:u}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Make your first call"}),e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["Replace ",e.jsx("code",{children:"your-model"})," with a model from the Models page."]})]}),e.jsx(K,{label:"curl",value:y,multiline:!0}),e.jsx(K,{label:"Python (OpenAI SDK)",value:p,multiline:!0})]}),e.jsx("div",{className:"flex justify-end",children:e.jsx(g,{variant:"primary",onPress:n,children:"I’ve saved this key"})})]})})}function V({trigger:t,message:a,confirmLabel:n,isPending:i,onConfirm:u}){const[o,c]=l.useState(!1);return o?e.jsxs("div",{className:"flex flex-col items-end gap-1.5 rounded-lg border border-amber-200 bg-amber-50 p-2 text-right",children:[e.jsx("span",{className:"max-w-xs text-xs text-amber-800",children:a}),e.jsxs("span",{className:"inline-flex gap-1",children:[e.jsx(g,{size:"sm",variant:"danger",isDisabled:i,onPress:u,children:n}),e.jsx(g,{size:"sm",variant:"ghost",isDisabled:i,onPress:()=>c(!1),children:"Cancel"})]})]}):e.jsx(g,{size:"sm",variant:"danger-soft",onPress:()=>c(!0),children:t})}function G({userId:t,users:a}){const n=t.trim();if(n==="")return e.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:"Choose an owner above to see the models this key can inherit."});const i=a.find(c=>c.user_id===n);if(!i)return e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["New user ",e.jsx("code",{children:n})," starts unrestricted, so this key may allow any model."]});const{text:u}=F(i.allowed_models),o=i.allowed_models&&i.allowed_models.length>0?i.allowed_models.join(", "):null;return e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["Owner ",e.jsx("code",{children:n})," allows ",e.jsx("span",{className:"font-medium text-[var(--otari-ink)]",children:u.toLowerCase()}),o?e.jsxs(e.Fragment,{children:[" (",e.jsx("span",{className:"font-mono",children:o}),")"]}):null,". This key inherits that, or narrows within it."]})}function Y({checked:t,onChange:a}){return e.jsxs("label",{className:"flex items-start gap-2 rounded-lg border border-[var(--otari-line)] p-3 text-sm",children:[e.jsx("input",{type:"checkbox",checked:t,onChange:n=>a(n.target.checked),className:"mt-0.5 h-4 w-4 accent-[var(--otari-brand)]","aria-label":"Exempt this key from budget"}),e.jsxs("span",{className:"flex flex-col gap-0.5",children:[e.jsx("span",{className:"font-medium text-[var(--otari-ink)]",children:"Exempt from budget"}),e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Requests on this key are logged with their cost but never counted toward the owner's budget or spend, and never blocked by it."})]})]})}function be({onClose:t,onCreated:a}){const n=se(),i=U(),[u,o]=l.useState(""),[c,x]=l.useState(""),[y,p]=l.useState(!1),[d,h]=l.useState(""),[f,r]=l.useState(null),[m,v]=l.useState(!1),[j,E]=l.useState(!0),C=c!==""&&new Date(c).getTime(){if(n.isPending||!j||N)return;const b={key_name:u.trim()||null,user_id:d.trim(),expires_at:c?new Date(c).toISOString():null,allowed_models:f,exclude_from_budget:m};n.mutate(b,{onSuccess:w=>{a(w),t()}})};return e.jsx(I,{children:e.jsxs(I.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsx("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Create API key"}),e.jsx(O,{error:n.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(A,{label:"Name",value:u,onChange:o,placeholder:"ci-bot",autoFocus:!0,description:"A label to recognize this key later."}),e.jsx(A,{label:"Expires (optional)",value:c,onChange:x,type:"datetime-local",description:C?e.jsx("span",{className:"text-red-700",children:"That time is in the past; the key would be rejected immediately."}):"Leave blank for a key that never expires."})]}),e.jsx(he,{value:d,onChange:h,users:i.data??[]}),e.jsx("button",{type:"button",className:"self-start text-xs font-medium text-[var(--otari-brand-dark)]",onClick:()=>p(b=>!b),children:y?"Hide advanced":"Advanced"}),y?e.jsxs("div",{className:"flex flex-col gap-4 rounded-lg border border-[var(--otari-line)] p-4",children:[e.jsx(G,{userId:d,users:i.data??[]}),e.jsx(W,{title:"Restrict this key's models",description:"By default this key inherits its owner's access. Optionally narrow it to a subset; a key can never exceed its owner's allowed models.",anyLabel:"Inherit owner access",initial:null,onChange:(b,w)=>{r(b),E(w)}}),e.jsx(Y,{checked:m,onChange:v})]}):null,e.jsxs("div",{className:"flex gap-2",children:[e.jsx(g,{variant:"primary",isDisabled:n.isPending||!j||N,onPress:S,children:n.isPending?"Creating…":"Create key"}),e.jsx(g,{variant:"ghost",onPress:t,children:"Cancel"})]})]})})}function we({apiKey:t,onClose:a}){const n=q(),i=U(),[u,o]=l.useState(t.key_name??""),[c,x]=l.useState(ge(t.expires_at)),[y,p]=l.useState(t.allowed_models),[d,h]=l.useState(t.exclude_from_budget),[f,r]=l.useState(!0),m=()=>{n.isPending||!f||n.mutate({id:t.id,body:{key_name:u.trim()||null,expires_at:c?new Date(c).toISOString():null,allowed_models:y,exclude_from_budget:d}},{onSuccess:a})};return e.jsx(I,{children:e.jsxs(I.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsxs("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:["Edit ",e.jsx("code",{children:t.key_name??t.id})]}),e.jsx(O,{error:n.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(A,{label:"Name",value:u,onChange:o,placeholder:"ci-bot"}),e.jsx(A,{label:"Expires",value:c,onChange:x,type:"datetime-local",description:"Blank clears the expiry."})]}),t.user_id?e.jsx(G,{userId:t.user_id,users:i.data??[]}):null,e.jsx(W,{title:"Restrict this key's models",description:"This key inherits its owner's access by default. Narrow it to a subset here; it can never exceed the owner's allowed models.",anyLabel:"Inherit owner access",initial:t.allowed_models,onChange:(v,j)=>{p(v),r(j)}}),e.jsx(Y,{checked:d,onChange:h}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(g,{variant:"primary",isDisabled:n.isPending||!f,onPress:m,children:n.isPending?"Saving…":"Save changes"}),e.jsx(g,{variant:"ghost",onPress:a,children:"Cancel"})]})]})})}function Ne({apiKey:t}){return t.is_active?pe(t)?e.jsx(D,{size:"sm",color:"warning",children:"Expired"}):e.jsx(D,{size:"sm",color:"accent",children:"Active"}):e.jsx(D,{size:"sm",color:"default",children:"Disabled"})}function ke({allowed:t}){const{text:a,tone:n}=F(t),i=n==="danger"?"text-red-700 font-medium":n==="muted"?"text-[var(--otari-muted)]":"text-[var(--otari-brand-dark)] font-medium",u=t&&t.length>0?t.join(", "):void 0;return e.jsx("span",{className:`text-xs ${i}`,title:u,children:a})}function Be(){const t=Q(),a=q(),n=X(),i=Z(),[u,o]=l.useState(!1),[c,x]=l.useState(null),[y,p]=l.useState(null),d=t.data??[],h=t.isLoading,f=d.find(s=>s.id===c)??null,r=!h&&d.length===0&&!u,m=ne(),[v,j]=l.useState(!1),[E,C]=l.useState(void 0),[N,S]=l.useState(!1),b=d.map(s=>s.id),w=re(m.selectedKeys,b),T=d.filter(s=>w.includes(s.id)),z=l.useCallback((s,k)=>a.mutate({id:s.id,body:{is_active:k}}),[a.mutate]),$=l.useCallback(s=>n.mutate(s.id,{onSuccess:k=>p({title:`New secret for ${M(s)}`,result:k})}),[n.mutate]),B=async(s,k,R)=>{S(!0),C(void 0);try{for(const L of s)await k(L);m.clear(),R==null||R()}catch(L){C(L)}finally{S(!1)}},J=l.useMemo(()=>[{id:"name",header:"Name",isRowHeader:!0,cell:s=>e.jsxs("div",{className:"flex flex-col gap-0.5",children:[e.jsx("span",{className:"font-medium text-[var(--otari-ink)]",children:s.key_name??e.jsx("span",{className:"text-[var(--otari-muted)]",children:"(unnamed)"})}),e.jsxs("div",{className:"flex flex-wrap items-center gap-1",children:[e.jsx(ke,{allowed:s.allowed_models}),s.exclude_from_budget?e.jsx("span",{className:"inline-flex items-center rounded-full border border-[var(--otari-line)] bg-[var(--otari-brand-tint)] px-2 py-0.5 text-xs font-medium text-[var(--otari-brand-dark)]",title:"Requests on this key are logged with cost but never counted toward budget",children:"Budget-exempt"}):null]})]})},{id:"status",header:"Status",cell:s=>e.jsx(Ne,{apiKey:s})},{id:"owner",header:"Owner",cell:s=>ye(s.user_id)?e.jsx(D,{size:"sm",color:"default",children:"virtual"}):e.jsx("code",{className:"text-xs text-[var(--otari-muted)]",children:s.user_id??"—"})},{id:"key",header:"Key",cell:s=>e.jsx("code",{className:"text-xs text-[var(--otari-muted)]",children:s.key_prefix?`${s.key_prefix}…`:"—"})},{id:"created",header:"Created",cell:s=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:H(s.created_at)})},{id:"last_used",header:"Last used",cell:s=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:fe(s.last_used_at)??"never"})},{id:"expires",header:"Expires",cell:s=>e.jsx("span",{className:"text-[var(--otari-muted)]",title:s.expires_at?new Date(s.expires_at).toLocaleString():void 0,children:s.expires_at?H(s.expires_at):"never"})},{id:"actions",header:"Actions",align:"end",cell:s=>e.jsxs("div",{className:"flex items-center justify-end gap-1.5",children:[e.jsx(g,{size:"sm",variant:"outline",isDisabled:a.isPending,onPress:()=>z(s,!s.is_active),children:s.is_active?"Disable":"Enable"}),e.jsx(g,{size:"sm",variant:"ghost",onPress:()=>{o(!1),x(s.id)},children:"Edit"}),e.jsx(V,{trigger:"Regenerate",confirmLabel:"Regenerate",isPending:n.isPending,message:e.jsxs(e.Fragment,{children:["Regenerate the secret for ",e.jsx("strong",{children:M(s)}),"? The current secret stops working immediately, with no grace period."]}),onConfirm:()=>$(s)}),s.is_active?null:e.jsx(V,{trigger:"Delete",confirmLabel:"Delete permanently",isPending:i.isPending,message:e.jsxs(e.Fragment,{children:["Permanently delete ",e.jsx("strong",{children:M(s)}),"? This removes the key and unlinks its usage history. Cannot be undone."]}),onConfirm:()=>i.mutate(s.id)})]})}],[a.isPending,n.isPending,i.isPending,i.mutate,z,$]),_=T.filter(s=>!s.is_active);return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(ee,{title:"API keys",description:"Issue and revoke the keys that authenticate callers to this gateway. Secrets are shown once at creation.",action:u?null:e.jsx(g,{variant:"primary",onPress:()=>{x(null),o(!0)},children:"Create key"})}),e.jsx(O,{error:t.error??a.error??n.error??i.error}),r?e.jsx(te,{title:"No API keys yet",description:"An API key authenticates callers to this gateway. Create one to make your first request; the secret is shown once, so keep it somewhere safe.",actionLabel:"Create your first key",onAction:()=>{x(null),o(!0)}}):null,u?e.jsx(be,{onClose:()=>o(!1),onCreated:s=>p({title:"API key created",result:s})}):null,f?e.jsx(we,{apiKey:f,onClose:()=>x(null)},f.id):null,w.length>0?e.jsxs(ie,{selectedCount:w.length,allMatching:!1,matchingTotal:null,canSelectAllMatching:!1,onSelectAllMatching:()=>{},onClear:m.clear,children:[e.jsx(g,{size:"sm",variant:"outline",isDisabled:N,onPress:()=>void B(T,s=>a.mutateAsync({id:s.id,body:{is_active:!1}})),children:"Disable"}),e.jsx(g,{size:"sm",variant:"outline",isDisabled:N,onPress:()=>void B(T,s=>a.mutateAsync({id:s.id,body:{exclude_from_budget:!0}})),children:"Budget-exempt"}),e.jsx(g,{size:"sm",variant:"danger",isDisabled:_.length===0,onPress:()=>j(!0),children:"Delete"})]}):null,r?null:e.jsx(oe,{ariaLabel:"API keys",columns:J,rows:d,getRowKey:je,isLoading:h,emptyContent:"No API keys yet. Create one to authenticate a caller.",selectionMode:"multiple",selectedKeys:m.selectedKeys,onSelectionChange:m.onSelectionChange}),e.jsx(le,{isOpen:v,onOpenChange:j,heading:"Delete API keys",body:`Permanently delete ${_.length} disabled ${_.length===1?"key":"keys"}? This removes them and unlinks their usage history. Cannot be undone. Active keys in the selection are skipped; disable them first.`,confirmLabel:"Delete permanently",isPending:N,error:E,onConfirm:()=>void B(_,s=>i.mutateAsync(s.id),()=>j(!1))}),y?e.jsx(ve,{title:y.title,result:y.result,onClose:()=>{p(null),n.reset()}}):null]})}export{Be as KeysPage}; diff --git a/src/gateway/static/dashboard/assets/ModelScopeControl-u-XlXgXz.js b/src/gateway/static/dashboard/assets/ModelScopeControl-DYPdGezt.js similarity index 88% rename from src/gateway/static/dashboard/assets/ModelScopeControl-u-XlXgXz.js rename to src/gateway/static/dashboard/assets/ModelScopeControl-DYPdGezt.js index 89f313b3..5fe4703d 100644 --- a/src/gateway/static/dashboard/assets/ModelScopeControl-u-XlXgXz.js +++ b/src/gateway/static/dashboard/assets/ModelScopeControl-DYPdGezt.js @@ -1 +1 @@ -import{j as t}from"./tanstack-query-1t81HyiD.js";import{r as n}from"./react-dgEcD0HR.js";import{L as A,h as $,i as P}from"./index-DV8qvdUj.js";import{C as d,I as R,a as T,b as V}from"./heroui-BI50yK5B.js";function q(r){return r===null?"any":r.length===0?"block":"only"}const O=50;function W({initial:r,onChange:m,title:N="Model access",description:w,anyLabel:C="Any model"}){const x=A(),u=$(),v=P(),[i,L]=n.useState(q(r)),[a,g]=n.useState(r??[]),[p,y]=n.useState(""),f=n.useMemo(()=>{var j,k;const e=new Set,s=[],l=(o,c)=>{o&&!e.has(o)&&(e.add(o),s.push({id:o,label:c}))};for(const o of((j=x.data)==null?void 0:j.providers)??[])l(`${o.instance}:*`,`${o.instance}:* · all ${o.instance} models`);for(const o of((k=u.data)==null?void 0:k.providers)??[])for(const c of o.models)l(c.key,c.key);for(const o of v.data??[])l(o.target,`${o.name} · alias`);return s},[x.data,u.data,v.data]),S=n.useMemo(()=>{const e=p.trim().toLowerCase();return f.filter(s=>!a.includes(s.id)).filter(s=>!e||s.id.toLowerCase().includes(e)||s.label.toLowerCase().includes(e)).slice(0,O)},[f,a,p]),h=(e,s)=>{e==="any"?m(null,!0):e==="block"?m([],!0):m(s,s.length>0)},B=e=>{L(e),h(e,a)},M=e=>{const s=a.includes(e)?a:[...a,e];g(s),y(""),h("only",s)},E=e=>{const s=a.filter(l=>l!==e);g(s),h("only",s)},b=(e,s)=>t.jsx("button",{type:"button","aria-pressed":i===e,onClick:()=>B(e),className:i===e?"rounded-md bg-white px-3 py-1.5 text-sm font-medium text-[var(--otari-ink)] shadow-sm":"rounded-md px-3 py-1.5 text-sm text-[var(--otari-muted)] hover:text-[var(--otari-ink)]",children:s}),I=!u.isLoading&&!x.isLoading&&f.length===0;return t.jsxs("div",{className:"flex flex-col gap-3",children:[t.jsxs("div",{children:[t.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:N}),t.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:w??"Which models this key may list and call. The master key is never restricted, so blocking a key cannot lock you out of the dashboard."})]}),t.jsxs("div",{className:"flex w-fit items-center gap-1 rounded-lg bg-[var(--otari-bg)] p-1",children:[b("any",C),b("only","Only selected"),b("block","Block all")]}),i==="block"?t.jsxs("div",{className:"rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800",children:["Blocked from ",t.jsx("strong",{children:"every"})," model until you change this access."]}):null,i==="only"?t.jsxs("div",{className:"flex flex-col gap-2",children:[t.jsx("div",{className:"flex flex-wrap gap-1.5",children:a.length===0?t.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Pick at least one model below, or choose “Block all”."}):a.map(e=>t.jsxs("span",{className:"inline-flex items-center gap-1 rounded-full bg-[var(--otari-brand-tint)] px-2.5 py-1 font-mono text-xs text-[var(--otari-brand-dark)]",children:[e,t.jsx("button",{type:"button","aria-label":`Remove ${e}`,onClick:()=>E(e),className:"text-[var(--otari-brand-dark)] hover:text-red-700",children:"×"})]},e))}),I?t.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"No providers or models discovered yet. Configure a provider first, then scope this key."}):t.jsxs(d.Root,{allowsEmptyCollection:!0,menuTrigger:"input",inputValue:p,onInputChange:y,selectedKey:null,onSelectionChange:e=>{e!=null&&M(String(e))},className:"flex max-w-md flex-col gap-1",children:[t.jsxs(d.InputGroup,{children:[t.jsx(R,{"aria-label":"Add a model",placeholder:"Search providers, models, aliases…",autoComplete:"off"}),t.jsx(d.Trigger,{})]}),t.jsx(d.Popover,{children:t.jsx(T,{items:S,className:"max-h-72 overflow-auto",children:e=>t.jsx(V,{id:e.id,textValue:e.label,children:e.label})})})]})]}):null]})}function X(r){return r===null?{text:"All models",tone:"muted"}:r.length===0?{text:"No models",tone:"danger"}:{text:"Selected models",tone:"normal"}}export{W as M,X as a}; +import{j as t}from"./tanstack-query-1t81HyiD.js";import{r as n}from"./react-dgEcD0HR.js";import{T as A,l as $,m as T}from"./index-Br3lxdY5.js";import{C as d,I as P,a as R,b as V}from"./heroui-BI50yK5B.js";function q(r){return r===null?"any":r.length===0?"block":"only"}const O=50;function W({initial:r,onChange:m,title:N="Model access",description:w,anyLabel:C="Any model"}){const x=A(),u=$(),v=T(),[i,S]=n.useState(q(r)),[a,g]=n.useState(r??[]),[p,y]=n.useState(""),f=n.useMemo(()=>{var j,k;const e=new Set,s=[],l=(o,c)=>{o&&!e.has(o)&&(e.add(o),s.push({id:o,label:c}))};for(const o of((j=x.data)==null?void 0:j.providers)??[])l(`${o.instance}:*`,`${o.instance}:* · all ${o.instance} models`);for(const o of((k=u.data)==null?void 0:k.providers)??[])for(const c of o.models)l(c.key,c.key);for(const o of v.data??[])l(o.target,`${o.name} · alias`);return s},[x.data,u.data,v.data]),L=n.useMemo(()=>{const e=p.trim().toLowerCase();return f.filter(s=>!a.includes(s.id)).filter(s=>!e||s.id.toLowerCase().includes(e)||s.label.toLowerCase().includes(e)).slice(0,O)},[f,a,p]),h=(e,s)=>{e==="any"?m(null,!0):e==="block"?m([],!0):m(s,s.length>0)},B=e=>{S(e),h(e,a)},M=e=>{const s=a.includes(e)?a:[...a,e];g(s),y(""),h("only",s)},E=e=>{const s=a.filter(l=>l!==e);g(s),h("only",s)},b=(e,s)=>t.jsx("button",{type:"button","aria-pressed":i===e,onClick:()=>B(e),className:i===e?"rounded-md bg-white px-3 py-1.5 text-sm font-medium text-[var(--otari-ink)] shadow-sm":"rounded-md px-3 py-1.5 text-sm text-[var(--otari-muted)] hover:text-[var(--otari-ink)]",children:s}),I=!u.isLoading&&!x.isLoading&&f.length===0;return t.jsxs("div",{className:"flex flex-col gap-3",children:[t.jsxs("div",{children:[t.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:N}),t.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:w??"Which models this key may list and call. The master key is never restricted, so blocking a key cannot lock you out of the dashboard."})]}),t.jsxs("div",{className:"flex w-fit items-center gap-1 rounded-lg bg-[var(--otari-bg)] p-1",children:[b("any",C),b("only","Only selected"),b("block","Block all")]}),i==="block"?t.jsxs("div",{className:"rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800",children:["Blocked from ",t.jsx("strong",{children:"every"})," model until you change this access."]}):null,i==="only"?t.jsxs("div",{className:"flex flex-col gap-2",children:[t.jsx("div",{className:"flex flex-wrap gap-1.5",children:a.length===0?t.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Pick at least one model below, or choose “Block all”."}):a.map(e=>t.jsxs("span",{className:"inline-flex items-center gap-1 rounded-full bg-[var(--otari-brand-tint)] px-2.5 py-1 font-mono text-xs text-[var(--otari-brand-dark)]",children:[e,t.jsx("button",{type:"button","aria-label":`Remove ${e}`,onClick:()=>E(e),className:"text-[var(--otari-brand-dark)] hover:text-red-700",children:"×"})]},e))}),I?t.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"No providers or models discovered yet. Configure a provider first, then scope this key."}):t.jsxs(d.Root,{allowsEmptyCollection:!0,menuTrigger:"input",inputValue:p,onInputChange:y,selectedKey:null,onSelectionChange:e=>{e!=null&&M(String(e))},className:"flex max-w-md flex-col gap-1",children:[t.jsxs(d.InputGroup,{children:[t.jsx(P,{"aria-label":"Add a model",placeholder:"Search providers, models, aliases…",autoComplete:"off"}),t.jsx(d.Trigger,{})]}),t.jsx(d.Popover,{children:t.jsx(R,{items:L,className:"max-h-72 overflow-auto",children:e=>t.jsx(V,{id:e.id,textValue:e.label,children:e.label})})})]})]}):null]})}function X(r){return r===null?{text:"All models",tone:"muted"}:r.length===0?{text:"No models",tone:"danger"}:{text:"Selected models",tone:"normal"}}export{W as M,X as a}; diff --git a/src/gateway/static/dashboard/assets/ModelsPage-BkzSZx5b.js b/src/gateway/static/dashboard/assets/ModelsPage-Dt9ZAYGy.js similarity index 86% rename from src/gateway/static/dashboard/assets/ModelsPage-BkzSZx5b.js rename to src/gateway/static/dashboard/assets/ModelsPage-Dt9ZAYGy.js index 0d515be0..7c7785fa 100644 --- a/src/gateway/static/dashboard/assets/ModelsPage-BkzSZx5b.js +++ b/src/gateway/static/dashboard/assets/ModelsPage-Dt9ZAYGy.js @@ -1 +1 @@ -import{j as e}from"./tanstack-query-1t81HyiD.js";import{i as gt,u as vt,r as c}from"./react-dgEcD0HR.js";import{x as _t,y as bt,h as jt,z as yt,A as je,P as Pt,E as Nt,F as B,I as St,B as ae,D as Re,C as ze,G as be,H as Ct,J as kt,K as E}from"./index-DV8qvdUj.js";import{u as Mt,r as Wt,B as It}from"./tableSelection-9hV37uhu.js";import{D as Tt}from"./DataTable-Bmn_eYSV.js";import{T as Lt,S as At}from"./TablePagination-CXhesdmH.js";import{B as A,d as se,f as V}from"./heroui-BI50yK5B.js";function Oe(t){const i=t.indexOf(":");return i>0?t.slice(0,i):"—"}function $t(t,i=Date.now()){const r=new Map;for(const n of t){const a=r.get(n.model_key)??[];a.push(n),r.set(n.model_key,a)}const s=[];for(const n of r.values()){const a=[...n].sort((p,u)=>Date.parse(p.effective_at)-Date.parse(u.effective_at)),f=[...a].reverse().find(p=>Date.parse(p.effective_at)<=i);s.push(f??a[0])}return s.sort((n,a)=>n.model_key.localeCompare(a.model_key))}const Et="otari",De=[{value:"vision",label:"Vision",test:t=>Array.isArray(t.input_modalities)&&t.input_modalities.includes("image")},{value:"tool_call",label:"Tool calling",test:t=>!!t.tool_call},{value:"reasoning",label:"Reasoning",test:t=>!!t.reasoning},{value:"structured_output",label:"Structured output",test:t=>!!t.structured_output},{value:"attachment",label:"Attachments",test:t=>!!t.attachment},{value:"audio",label:"Audio",test:t=>Array.isArray(t.input_modalities)&&t.input_modalities.includes("audio")},{value:"pdf",label:"PDF",test:t=>Array.isArray(t.input_modalities)&&t.input_modalities.includes("pdf")}],Ot=[{key:"reasoning",label:"Reasoning"},{key:"tool_call",label:"Tool calling"},{key:"structured_output",label:"Structured output"},{key:"attachment",label:"Attachments"},{key:"temperature",label:"Temperature"}],Fe={text:"Text",image:"Image",audio:"Audio",video:"Video",pdf:"PDF"},Dt=[{value:"0",label:"Any context"},{value:"8000",label:"≥ 8K"},{value:"32000",label:"≥ 32K"},{value:"128000",label:"≥ 128K"},{value:"200000",label:"≥ 200K"},{value:"1000000",label:"≥ 1M"}],Ft=[{value:"",label:"Any price"},{value:"1",label:"≤ $1 / 1M in"},{value:"3",label:"≤ $3 / 1M in"},{value:"10",label:"≤ $10 / 1M in"},{value:"30",label:"≤ $30 / 1M in"}],Bt=[{value:"",label:"Base prices"},{value:"8000",label:"Compare at 8K"},{value:"128000",label:"Compare at 128K"},{value:"200000",label:"Compare at 200K"},{value:"500000",label:"Compare at 500K"},{value:"1000000",label:"Compare at 1M"}],Kt=[{value:"all",label:"Any release date"},{value:"365",label:"Past year"},{value:"730",label:"Past 2 years"},{value:"1095",label:"Past 3 years"}],Rt=1440*60*1e3,zt=t=>t.key;function Be(t,i){const r=`${i}:`;return t.startsWith(r)?t.slice(r.length):t}function Ke(t,i){const r={inputPrice:t.inputPrice,outputPrice:t.outputPrice,cacheReadPrice:t.cacheReadPrice,cacheWritePrice:t.cacheWritePrice,cacheWrite1hPrice:t.cacheWrite1hPrice};if(i==null)return r;const s=t.pricingTiers.filter(n=>n.min_input_tokens<=i).sort((n,a)=>a.min_input_tokens-n.min_input_tokens)[0];return s?{inputPrice:s.input_price_per_million??r.inputPrice,outputPrice:s.output_price_per_million??r.outputPrice,cacheReadPrice:s.cache_read_price_per_million??r.cacheReadPrice,cacheWritePrice:s.cache_write_price_per_million??r.cacheWritePrice,cacheWrite1hPrice:s.cache_write_1h_price_per_million??r.cacheWrite1hPrice}:r}function ce(t){const i=Number(t);return t.trim()!==""&&Number.isFinite(i)&&i>=0}function w(t){if(t.trim()==="")return!0;const i=Number(t);return Number.isFinite(i)&&i>=0}function Y(t){return t.trim()===""?null:Number(t)}function we(t){return t.map((i,r)=>({id:r,minInputTokens:String(i.min_input_tokens),input:i.input_price_per_million==null?"":String(i.input_price_per_million),output:i.output_price_per_million==null?"":String(i.output_price_per_million),cacheRead:i.cache_read_price_per_million==null?"":String(i.cache_read_price_per_million),cacheWrite:i.cache_write_price_per_million==null?"":String(i.cache_write_price_per_million),cacheWrite1h:i.cache_write_1h_price_per_million==null?"":String(i.cache_write_1h_price_per_million)}))}function Ve(t){const i=new Set;return t.every(r=>{const s=Number(r.minInputTokens),n=[r.input,r.output,r.cacheRead,r.cacheWrite,r.cacheWrite1h].some(a=>a.trim()!=="");return!Number.isInteger(s)||s<=0||i.has(s)||!n?!1:(i.add(s),[r.input,r.output,r.cacheRead,r.cacheWrite,r.cacheWrite1h].every(w))})}function Ye(t){return t.map(i=>({min_input_tokens:Number(i.minInputTokens),...i.input.trim()===""?{}:{input_price_per_million:Number(i.input)},...i.output.trim()===""?{}:{output_price_per_million:Number(i.output)},...i.cacheRead.trim()===""?{}:{cache_read_price_per_million:Number(i.cacheRead)},...i.cacheWrite.trim()===""?{}:{cache_write_price_per_million:Number(i.cacheWrite)},...i.cacheWrite1h.trim()===""?{}:{cache_write_1h_price_per_million:Number(i.cacheWrite1h)}}))}function b({value:t,onChange:i,ariaLabel:r}){return e.jsx("input",{type:"number",step:"any",min:"0",inputMode:"decimal","aria-label":r,value:t,onChange:s=>i(s.target.value),className:"w-28 rounded-md border border-[var(--otari-line)] bg-white px-2 py-1 text-right text-sm tabular-nums focus:border-[var(--otari-brand)] focus:outline-none"})}function He({tiers:t,onChange:i}){const r=(n,a,f)=>{i(t.map(p=>p.id===n?{...p,[a]:f}:p))},s=()=>{const n=t.reduce((a,f)=>Math.max(a,f.id),-1)+1;i([...t,{id:n,minInputTokens:"128000",input:"",output:"",cacheRead:"",cacheWrite:"",cacheWrite1h:""}])};return e.jsxs("div",{className:"flex flex-col gap-2 rounded-lg border border-[var(--otari-line)] p-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-xs font-medium text-[var(--otari-ink)]",children:"Long-context price tiers"}),e.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:"At a threshold, listed rates replace the base rate for the whole request."})]}),e.jsx(A,{size:"sm",variant:"outline",onPress:s,children:"Add tier"})]}),t.map(n=>e.jsxs("div",{className:"flex flex-wrap items-end gap-2 border-t border-[var(--otari-line)] pt-2",children:[e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["Context ≥ tokens",e.jsx("input",{type:"number",min:"1",step:"1",inputMode:"numeric","aria-label":"Tier context threshold",value:n.minInputTokens,onChange:a=>r(n.id,"minInputTokens",a.target.value),className:"w-28 rounded-md border border-[var(--otari-line)] bg-white px-2 py-1 text-right text-sm tabular-nums focus:border-[var(--otari-brand)] focus:outline-none"})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["Input",e.jsx(b,{value:n.input,onChange:a=>r(n.id,"input",a),ariaLabel:"Tier input price"})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["Output",e.jsx(b,{value:n.output,onChange:a=>r(n.id,"output",a),ariaLabel:"Tier output price"})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["Cache read",e.jsx(b,{value:n.cacheRead,onChange:a=>r(n.id,"cacheRead",a),ariaLabel:"Tier cache read price"})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["Cache write",e.jsx(b,{value:n.cacheWrite,onChange:a=>r(n.id,"cacheWrite",a),ariaLabel:"Tier cache write price"})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["1h write",e.jsx(b,{value:n.cacheWrite1h,onChange:a=>r(n.id,"cacheWrite1h",a),ariaLabel:"Tier 1 hour cache write price"})]}),e.jsx(A,{size:"sm",variant:"ghost",onPress:()=>i(t.filter(a=>a.id!==n.id)),children:"Remove"})]},n.id))]})}function wt({source:t}){return t==="configured"?e.jsx(V,{size:"sm",color:"default",children:"configured"}):t==="default"||t==="alias"?e.jsx(V,{size:"sm",color:"accent",children:t}):e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"not priced"})}function oe({label:t,tone:i="info",children:r}){const s=c.useId();return e.jsxs("span",{className:"group relative inline-flex items-center font-normal normal-case",children:[e.jsx("button",{type:"button","aria-label":t,"aria-describedby":s,className:`inline-flex h-4 w-4 items-center justify-center rounded-full border text-[10px] leading-none ${i==="warning"?"border-[#c2843a] text-[#b45309]":"border-[var(--otari-line)] text-[var(--otari-muted)] hover:border-[var(--otari-brand)] hover:text-[var(--otari-brand)]"}`,children:"i"}),e.jsx("span",{id:s,role:"tooltip",className:"pointer-events-none absolute top-full right-0 z-20 mt-1.5 w-72 rounded-lg border border-[var(--otari-line)] bg-[var(--otari-surface)] px-3 py-2 text-left text-xs font-normal whitespace-normal break-words text-[var(--otari-ink)] opacity-0 shadow-lg transition-opacity group-hover:opacity-100 group-focus-within:opacity-100",children:r})]})}function Vt(){const t=kt();return t.data?t.data.default_pricing?e.jsx(oe,{label:"How unpriced models are metered",tone:"info",children:"Default pricing is on: models without a configured price are metered using community-maintained rates (the bundled genai-prices dataset). Set a price to override the fallback."}):e.jsxs(oe,{label:"How unpriced models are metered",tone:"warning",children:["Default pricing is off: only models with a configured price are metered.",t.data.require_pricing?" Requests for any other model are rejected (HTTP 402) because require_pricing is on.":" Other models are served without cost tracking."]}):null}function I({label:t,value:i}){return e.jsxs("div",{className:"flex items-baseline justify-between gap-3",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:t}),e.jsx("span",{className:"text-right text-sm text-[var(--otari-ink)] tabular-nums",children:i})]})}function le({title:t,children:i}){return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx("span",{className:"text-xs font-semibold uppercase tracking-wide text-[var(--otari-muted)]",children:t}),i]})}function Yt({row:t}){const i=je(),r=Re(),[s,n]=c.useState(!1),[a,f]=c.useState(""),[p,u]=c.useState(""),[P,j]=c.useState(""),[k,T]=c.useState(""),[y,h]=c.useState(""),[N,O]=c.useState([]),S=()=>{f(t.inputPrice==null?"":String(t.inputPrice)),u(t.outputPrice==null?"":String(t.outputPrice)),j(t.cacheReadPrice==null?"":String(t.cacheReadPrice)),T(t.cacheWritePrice==null?"":String(t.cacheWritePrice)),h(t.cacheWrite1hPrice==null?"":String(t.cacheWrite1hPrice)),O(we(t.pricingTiers)),n(!0)},Z=ce(a)&&ce(p)&&w(P)&&w(k)&&w(y)&&Ve(N),W=()=>{Z&&i.mutate({model_key:t.key,input_price_per_million:Number(a),output_price_per_million:Number(p),cache_read_price_per_million:Y(P),cache_write_price_per_million:Y(k),cache_write_1h_price_per_million:Y(y),pricing_tiers:Ye(N)},{onSuccess:()=>n(!1)})};return s?e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Input $ / 1M"}),e.jsx(b,{value:a,onChange:f,ariaLabel:`Input price for ${t.key}`})]}),e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Output $ / 1M"}),e.jsx(b,{value:p,onChange:u,ariaLabel:`Output price for ${t.key}`})]}),e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Cache read $ / 1M"}),e.jsx(b,{value:P,onChange:j,ariaLabel:`Cache read price for ${t.key}`})]}),e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Cache write $ / 1M"}),e.jsx(b,{value:k,onChange:T,ariaLabel:`Cache write price for ${t.key}`})]}),e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"1h cache write $ / 1M"}),e.jsx(b,{value:y,onChange:h,ariaLabel:`1 hour cache write price for ${t.key}`})]}),e.jsx(He,{tiers:N,onChange:O}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(A,{size:"sm",variant:"primary",isDisabled:i.isPending||!Z,onPress:W,children:"Save"}),e.jsx(A,{size:"sm",variant:"ghost",isDisabled:i.isPending,onPress:()=>n(!1),children:"Cancel"})]}),i.error?e.jsx("span",{className:"text-xs text-red-700",children:be(i.error)}):null]}):e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(I,{label:"Input",value:t.inputPrice==null?"—":`${E(t.inputPrice)} / 1M`}),e.jsx(I,{label:"Output",value:t.outputPrice==null?"—":`${E(t.outputPrice)} / 1M`}),e.jsx(I,{label:"Cache read",value:t.cacheReadPrice==null?"—":`${E(t.cacheReadPrice)} / 1M`}),e.jsx(I,{label:"Cache write",value:t.cacheWritePrice==null?"—":`${E(t.cacheWritePrice)} / 1M`}),e.jsx(I,{label:"1h cache write",value:t.cacheWrite1hPrice==null?"—":`${E(t.cacheWrite1hPrice)} / 1M`}),e.jsx(I,{label:"Context tiers",value:t.pricingTiers.length?`${t.pricingTiers.length} configured`:"—"}),e.jsxs("div",{className:"flex items-center gap-2 pt-1",children:[e.jsx(A,{size:"sm",variant:"outline",onPress:S,children:t.source==="configured"?"Edit price":"Set price"}),t.source==="configured"?e.jsxs(e.Fragment,{children:[e.jsx(ze,{confirmLabel:"Reset",isPending:r.isPending,onConfirm:()=>r.mutate(t.key),children:"Reset"}),e.jsx(oe,{label:"What reset does",children:"Removes the custom price. The model reverts to the default rate (genai-prices) when default pricing is on, otherwise it is metered at no cost."})]}):null,r.error?e.jsx("span",{className:"text-xs text-red-700",children:be(r.error)}):null]})]})}function Ht({row:t,metadata:i,metadataAvailable:r,onMakeAlias:s,onClose:n}){const a=(i==null?void 0:i.input_modalities)??[],f=(i==null?void 0:i.output_modalities)??[],p=Ot.filter(({key:u})=>i==null?void 0:i[u]);return e.jsx(se,{children:e.jsxs(se.Content,{className:"flex flex-col gap-5 p-5",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h2",{className:"text-base font-semibold break-all text-[var(--otari-ink)]",children:t.model}),i!=null&&i.deprecated?e.jsx(V,{size:"sm",color:"danger",children:"deprecated"}):null]}),e.jsxs("p",{className:"mt-1 text-xs break-all text-[var(--otari-muted)]",children:["Selector: ",e.jsx("code",{children:t.key})]}),i!=null&&i.family?e.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:i.family}):null]}),e.jsx("button",{type:"button","aria-label":"Close model details",onClick:n,className:"-mt-1 -mr-1 shrink-0 rounded-md px-1.5 py-0.5 text-lg leading-none text-[var(--otari-muted)] hover:bg-[var(--otari-bg)] hover:text-[var(--otari-ink)]",children:"✕"})]}),i!=null&&i.description?e.jsx("p",{className:"text-sm text-[var(--otari-ink)]",children:i.description}):null,e.jsxs(le,{title:"Pricing",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(wt,{source:t.source}),t.isDiscovered?null:e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"not discovered"})]}),e.jsx(Yt,{row:t},t.key),e.jsx(A,{size:"sm",variant:"outline",onPress:()=>s(t.key),children:"Make an alias"})]}),e.jsxs(le,{title:"Specs",children:[e.jsx(I,{label:"Context window",value:ae(t.contextWindow)}),e.jsx(I,{label:"Max output",value:ae((i==null?void 0:i.max_output_tokens)??null)}),e.jsx(I,{label:"Knowledge cutoff",value:(i==null?void 0:i.knowledge_cutoff)??"—"}),e.jsx(I,{label:"Released",value:Ct(i==null?void 0:i.release_date)}),e.jsx(I,{label:"Open weights",value:i?i.open_weights?"Yes":"No":"—"})]}),e.jsx(le,{title:"Modalities",children:a.length===0&&f.length===0?e.jsx("span",{className:"text-sm text-[var(--otari-muted)]",children:"Unknown."}):e.jsxs("div",{className:"flex flex-col gap-1.5 text-xs text-[var(--otari-muted)]",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-1",children:[e.jsx("span",{children:"In:"}),a.map(u=>e.jsx(V,{size:"sm",color:"default",children:Fe[u]??u},u))]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-1",children:[e.jsx("span",{children:"Out:"}),f.map(u=>e.jsx(V,{size:"sm",color:"default",children:Fe[u]??u},u))]})]})}),e.jsx(le,{title:"Capabilities",children:p.length>0?e.jsx("div",{className:"flex flex-wrap gap-1.5",children:p.map(({key:u,label:P})=>e.jsx(V,{size:"sm",color:"default",children:P},u))}):e.jsx("span",{className:"text-sm text-[var(--otari-muted)]",children:r?"None reported.":"Extended metadata unavailable (models.dev disabled or unreachable)."})})]})})}const qt=15;function Ut({value:t,onChange:i,placeholder:r}){return e.jsx("input",{type:"search",value:t,onChange:s=>i(s.target.value),placeholder:r,"aria-label":r,className:"w-full max-w-xs rounded-md border border-[var(--otari-line)] bg-white px-3 py-1.5 text-sm focus:border-[var(--otari-brand)] focus:outline-none"})}const qe="otari.dashboard.modelsSort",_e={col:"model",dir:"asc"},Gt=["model","released","input","output"];function Jt(){if(typeof window>"u")return _e;try{const t=window.localStorage.getItem(qe);if(!t)return _e;const i=JSON.parse(t);if(Gt.includes(i.col)&&(i.dir==="asc"||i.dir==="desc"))return{col:i.col,dir:i.dir}}catch{}return _e}function Xt({row:t,onClose:i}){const r=je(),s=Re(),[n,a]=c.useState(t.inputPrice==null?"":String(t.inputPrice)),[f,p]=c.useState(t.outputPrice==null?"":String(t.outputPrice)),[u,P]=c.useState(t.cacheReadPrice==null?"":String(t.cacheReadPrice)),[j,k]=c.useState(t.cacheWritePrice==null?"":String(t.cacheWritePrice)),[T,y]=c.useState(t.cacheWrite1hPrice==null?"":String(t.cacheWrite1hPrice)),[h,N]=c.useState(we(t.pricingTiers)),O=ce(n)&&ce(f)&&w(u)&&w(j)&&w(T)&&Ve(h),S=()=>{O&&r.mutate({model_key:t.key,input_price_per_million:Number(n),output_price_per_million:Number(f),cache_read_price_per_million:Y(u),cache_write_price_per_million:Y(j),cache_write_1h_price_per_million:Y(T),pricing_tiers:Ye(h)},{onSuccess:i})};return e.jsxs("div",{className:"flex flex-col gap-3 px-4 py-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("span",{className:"text-xs font-medium break-all text-[var(--otari-muted)]",children:t.key}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:["Input $ / 1M",e.jsx(b,{value:n,onChange:a,ariaLabel:`Input price for ${t.key}`})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:["Output $ / 1M",e.jsx(b,{value:f,onChange:p,ariaLabel:`Output price for ${t.key}`})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:["Cache read $ / 1M",e.jsx(b,{value:u,onChange:P,ariaLabel:`Cache read price for ${t.key}`})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:["Cache write $ / 1M",e.jsx(b,{value:j,onChange:k,ariaLabel:`Cache write price for ${t.key}`})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:["1h cache write $ / 1M",e.jsx(b,{value:T,onChange:y,ariaLabel:`1 hour cache write price for ${t.key}`})]}),e.jsx(A,{size:"sm",variant:"primary",isDisabled:r.isPending||!O,onPress:S,children:r.isPending?"Saving…":"Save"}),e.jsx(A,{size:"sm",variant:"ghost",isDisabled:r.isPending,onPress:i,children:"Cancel"}),t.source==="configured"?e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(ze,{confirmLabel:"Reset",isPending:s.isPending,onConfirm:()=>s.mutate(t.key,{onSuccess:i}),children:"Reset"}),e.jsx(oe,{label:"What reset does",children:"Removes the custom price. The model reverts to the default rate (genai-prices) when default pricing is on, otherwise it is metered at no cost."})]}):null,r.error||s.error?e.jsx("span",{className:"text-xs text-red-700",children:be(r.error??s.error)}):null]}),e.jsx(He,{tiers:h,onChange:N})]})}function Zt({primary:t,secondary:i,rowKey:r,primaryLabel:s,secondaryLabel:n,onEdit:a}){const f=(p,u)=>e.jsx("button",{type:"button","aria-label":`Edit ${u} price for ${r}`,className:"tabular-nums hover:text-[var(--otari-brand-dark)] hover:underline",onClick:P=>{P.stopPropagation(),a()},children:p==null?"—":E(p)});return e.jsxs("span",{className:"inline-flex items-center justify-end gap-1",children:[f(t,s),e.jsx("span",{className:"text-[var(--otari-muted)]",children:"/"}),f(i,n)]})}function Qt({rates:t,rowKey:i,onEdit:r}){const s=[t.cacheReadPrice==null?null:`R ${E(t.cacheReadPrice)}`,t.cacheWritePrice==null?null:`W ${E(t.cacheWritePrice)}`,t.cacheWrite1hPrice==null?null:`1h ${E(t.cacheWrite1hPrice)}`].filter(n=>n!==null);return e.jsx("button",{type:"button","aria-label":`Edit caching price for ${i}`,className:"max-w-44 text-right text-xs leading-5 text-[var(--otari-muted)] hover:text-[var(--otari-brand-dark)] hover:underline",onClick:n=>{n.stopPropagation(),r()},children:s.length>0?s.join(" · "):"Input-rate fallback"})}function ei({row:t,onEdit:i}){const r=[...t.pricingTiers].sort((n,a)=>n.min_input_tokens-a.min_input_tokens).map(n=>ae(n.min_input_tokens)),s=r.length===0?"Base only":`${r.length} tier${r.length===1?"":"s"} · ≥ ${r.join(", ")}`;return e.jsx("button",{type:"button","aria-label":`Edit pricing policy for ${t.key}`,className:"max-w-40 text-right text-xs leading-5 text-[var(--otari-muted)] hover:text-[var(--otari-brand-dark)] hover:underline",onClick:n=>{n.stopPropagation(),i()},children:s})}function ti({rows:t,isLoading:i,empty:r,sortDescriptor:s,onSortChange:n,selectedKey:a,onSelect:f,onEditPricing:p,comparisonContextTokens:u,selectedKeys:P,onSelectionChange:j}){const k=c.useMemo(()=>{const y=u==null?"Base":`at ${ae(u)}`;return[{id:"model",header:"Model",isRowHeader:!0,allowsSorting:!0,cell:h=>e.jsxs("span",{className:"font-medium break-all",children:[h.model,e.jsx("span",{className:"sr-only",children:h.key})]})},{id:"provider",header:"Provider",cell:h=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:h.provider})},{id:"input",header:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[`${y} in / out $ / 1M`,e.jsx(Vt,{})]}),align:"end",allowsSorting:!0,cell:h=>{const N=Ke(h,u);return e.jsx(Zt,{primary:N.inputPrice,secondary:N.outputPrice,rowKey:h.key,primaryLabel:"input",secondaryLabel:"output",onEdit:()=>p(h.key)})}},{id:"caching",header:`Caching ${u==null?"policy":y}`,align:"end",cell:h=>e.jsx(Qt,{rates:Ke(h,u),rowKey:h.key,onEdit:()=>p(h.key)})},{id:"policy",header:"Pricing policy",align:"end",cell:h=>e.jsx(ei,{row:h,onEdit:()=>p(h.key)})}]},[u,p]),T=c.useCallback(y=>y.key===a?"bg-[var(--otari-brand-tint)]":void 0,[a]);return e.jsx(Tt,{ariaLabel:"Models",columns:k,rows:t,getRowKey:zt,isLoading:i,emptyContent:r,selectionMode:"multiple",selectedKeys:P,onSelectionChange:j,sortDescriptor:s,onSortChange:n,onRowAction:f,rowClassName:T})}function ii({providers:t}){return t.length===0?null:e.jsxs(St,{tone:"warning",children:["Could not list ",t.map(i=>i.provider).join(", "),". Check that provider's credentials in config.yml; its models are missing from the list below."]})}function pi(){var Te,Le,Ae;const t=gt(),[i]=vt(),r=_t(),s=bt(),n=jt(),a=yt(),f=je(),[p,u]=c.useState(""),[P,j]=c.useState(0),[k,T]=c.useState(qt),[y,h]=c.useState(null),[N,O]=c.useState(null),[S,Z]=c.useState(Jt),W=Mt(),[Ue,ue]=c.useState(!1),[Ge,ye]=c.useState(!1),[Je,Pe]=c.useState(void 0);c.useEffect(()=>{try{window.localStorage.setItem(qe,JSON.stringify(S))}catch{}},[S]);const[D,Ne]=c.useState(i.get("provider")||"all"),[K,Xe]=c.useState("all"),[H,Ze]=c.useState("all"),[q,Qe]=c.useState("all"),[de,et]=c.useState("0"),[Q,tt]=c.useState(""),[ee,it]=c.useState("all"),[pe,rt]=c.useState(""),F=((Te=a.data)==null?void 0:Te.models)??{},nt=((Le=a.data)==null?void 0:Le.available)??!1,Se=c.useMemo(()=>{var l;return new Set((((l=n.data)==null?void 0:l.providers)??[]).flatMap(m=>m.models.map(o=>o.key)))},[n.data]),lt=l=>{u(l),j(0)},R=l=>m=>{l(m),j(0)},te=c.useMemo(()=>{var M,v,_,$,L,z,$e;const l=new Map($t(s.data??[]).map(d=>[d.model_key,d])),m=[],o=new Set,g=(d,ve,Ee,x)=>{if(o.has(d))return;o.add(d);const C=l.get(d);m.push({key:d,model:Be(ve,Ee),provider:Ee,isDiscovered:Se.has(d),contextWindow:(x==null?void 0:x.contextWindow)??null,inputPrice:C?C.input_price_per_million:(x==null?void 0:x.inputPrice)??null,outputPrice:C?C.output_price_per_million:(x==null?void 0:x.outputPrice)??null,cacheReadPrice:C?C.cache_read_price_per_million:(x==null?void 0:x.cacheReadPrice)??null,cacheWritePrice:C?C.cache_write_price_per_million:(x==null?void 0:x.cacheWritePrice)??null,cacheWrite1hPrice:C?C.cache_write_1h_price_per_million??null:(x==null?void 0:x.cacheWrite1hPrice)??null,pricingTiers:C?C.pricing_tiers??[]:(x==null?void 0:x.pricingTiers)??[],source:C?"configured":(x==null?void 0:x.source)??"none"})};for(const d of((M=r.data)==null?void 0:M.data)??[]){if(d.owned_by===Et)continue;const ve=d.pricing_source==="default"?"default":d.pricing?"configured":"none";g(d.id,d.id,d.owned_by||Oe(d.id),{key:d.id,model:d.id,provider:d.owned_by,contextWindow:d.context_window,inputPrice:((v=d.pricing)==null?void 0:v.input_price_per_million)??null,outputPrice:((_=d.pricing)==null?void 0:_.output_price_per_million)??null,cacheReadPrice:(($=d.pricing)==null?void 0:$.cache_read_price_per_million)??null,cacheWritePrice:((L=d.pricing)==null?void 0:L.cache_write_price_per_million)??null,cacheWrite1hPrice:((z=d.pricing)==null?void 0:z.cache_write_1h_price_per_million)??null,pricingTiers:(($e=d.pricing)==null?void 0:$e.pricing_tiers)??[],source:ve})}for(const d of l.keys())g(d,d,Oe(d));return m},[r.data,s.data,Se]),Ce=c.useMemo(()=>new Map(te.map(l=>[l.key,l])),[te]),ie=c.useMemo(()=>{var o,g,M;const l=te.map(v=>{var _,$;return{...v,contextWindow:v.contextWindow??((_=F[v.key])==null?void 0:_.context_window)??null,releaseDate:(($=F[v.key])==null?void 0:$.release_date)??null}}),m=new Set(l.map(v=>v.key));for(const v of((o=n.data)==null?void 0:o.providers)??[])for(const _ of v.models)m.has(_.key)||(m.add(_.key),l.push({key:_.key,model:Be(_.key,v.provider),provider:v.provider,isDiscovered:!0,contextWindow:((g=F[_.key])==null?void 0:g.context_window)??null,releaseDate:((M=F[_.key])==null?void 0:M.release_date)??null,inputPrice:null,outputPrice:null,cacheReadPrice:null,cacheWritePrice:null,cacheWrite1hPrice:null,pricingTiers:[],source:"none"}));return l},[te,n.data,F]),at=(((Ae=n.data)==null?void 0:Ae.providers)??[]).filter(l=>!l.ok),re=c.useMemo(()=>{const l=Array.from(new Set(ie.map(m=>m.provider))).sort((m,o)=>m.localeCompare(o));return[{value:"all",label:"All providers"},...l.map(m=>({value:m,label:m}))]},[ie]);c.useEffect(()=>{D==="all"||re.length<=1||re.some(l=>l.value===D)||Ne("all")},[re,D]);const U=p.trim().toLowerCase(),he=Number(de)||0,me=Q===""?Number.POSITIVE_INFINITY:Number(Q),st=pe===""?null:Number(pe),xe=ee==="all"?null:Date.now()-Number(ee)*Rt,G=c.useMemo(()=>{const l=o=>{if(U&&!o.key.toLowerCase().includes(U)&&!o.provider.toLowerCase().includes(U)||D!=="all"&&o.provider!==D||K==="configured"&&o.source!=="configured"||K==="default"&&o.source!=="default"||K==="priced"&&o.inputPrice==null||K==="unpriced"&&o.inputPrice!=null||H==="discovered"&&!o.isDiscovered||H==="custom"&&o.isDiscovered)return!1;if(q!=="all"){const g=De.find(v=>v.value===q),M=F[o.key];if(!g||!M||!g.test(M))return!1}if(he>0&&(o.contextWindow==null||o.contextWindowme))return!1;if(xe!=null){const g=o.releaseDate?Date.parse(o.releaseDate):Number.NaN;if(Number.isNaN(g)||g{const M=S.dir==="asc"?1:-1;if(S.col==="model")return o.model.localeCompare(g.model)*M;if(S.col==="released"){const L=o.releaseDate??null,z=g.releaseDate??null;return!L&&!z?o.model.localeCompare(g.model):L?z?(Lz?1:0)*M||o.model.localeCompare(g.model):-1:1}const v=L=>S.col==="input"?L.inputPrice:L.outputPrice,_=v(o),$=v(g);return _==null&&$==null?o.model.localeCompare(g.model):_==null?1:$==null?-1:(_-$)*M||o.model.localeCompare(g.model)};return ie.filter(l).sort(m)},[ie,U,D,K,H,q,he,me,xe,F,S]),J=G.length,ct=Math.max(1,Math.ceil(J/k)),ke=Math.min(P,ct-1),Me=ke*k,fe=G.slice(Me,Me+k),ot={column:S.col,direction:S.dir==="asc"?"ascending":"descending"},ut=l=>{Z({col:String(l.column),dir:l.direction==="ascending"?"asc":"desc"}),j(0)},dt=c.useCallback(l=>h(m=>m===l?null:l),[]),ge=fe.map(l=>l.key),X=Wt(W.selectedKeys,ge),pt=ge.length>0&&X.length===ge.length&&J>X.length,ht=W.allMatching?G.map(l=>l.key):X,We=W.allMatching?J:X.length,Ie=y?G.find(l=>l.key===y)??Ce.get(y)??null:null,mt=async l=>{ye(!0),Pe(void 0);try{for(const m of ht)await f.mutateAsync({model_key:m,input_price_per_million:l.input_price_per_million,output_price_per_million:l.output_price_per_million,cache_read_price_per_million:l.cache_read_price_per_million??null,cache_write_price_per_million:l.cache_write_price_per_million??null,cache_write_1h_price_per_million:null,pricing_tiers:[]});W.clear(),ue(!1)}catch(m){Pe(m)}finally{ye(!1)}},xt=r.isLoading||s.isLoading||n.isLoading,ft=U!==""||D!=="all"||K!=="all"||H!=="all"||q!=="all"||de!=="0"||Q!==""||ee!=="all"?"No models match your filters.":"No models yet. Add a provider on the Providers page, or price a model below.",ne=N?G.find(l=>l.key===N)??Ce.get(N)??null:null;return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(Pt,{title:"Models",description:"Every model your providers can serve. Set a price on any model so budgets and usage tracking work."}),e.jsx(Nt,{error:r.error??s.error??n.error??a.error}),e.jsxs("div",{className:`grid gap-4 lg:items-start ${ne?"lg:grid-cols-[minmax(0,1fr)_360px]":"grid-cols-1"}`,children:[e.jsxs("div",{className:"flex min-w-0 flex-col gap-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx(Ut,{value:p,onChange:lt,placeholder:"Search models…"}),e.jsx(B,{ariaLabel:"Filter by provider",value:D,onChange:R(Ne),options:re}),e.jsx(B,{ariaLabel:"Filter by pricing",value:K,onChange:R(Xe),options:[{value:"all",label:"Any pricing"},{value:"configured",label:"Custom price"},{value:"default",label:"Default price"},{value:"priced",label:"Priced"},{value:"unpriced",label:"Unpriced"}]}),e.jsx(B,{ariaLabel:"Filter by source",value:H,onChange:R(Ze),options:[{value:"all",label:"Any source"},{value:"discovered",label:"Discovered"},{value:"custom",label:"Custom (not discovered)"}]}),e.jsx(B,{ariaLabel:"Filter by capability",value:q,onChange:R(Qe),options:[{value:"all",label:"Any capability"},...De.map(l=>({value:l.value,label:l.label}))]}),e.jsx(B,{ariaLabel:"Minimum context window",value:de,onChange:R(et),options:Dt}),e.jsx(B,{ariaLabel:"Maximum input price",value:Q,onChange:R(tt),options:Ft}),e.jsx(B,{ariaLabel:"Compare prices at context",value:pe,onChange:rt,options:Bt}),e.jsx(B,{ariaLabel:"Filter by release date",value:ee,onChange:R(it),options:Kt})]}),e.jsx(ii,{providers:at}),X.length>0?e.jsx(It,{selectedCount:We,allMatching:W.allMatching,matchingTotal:J,canSelectAllMatching:pt,onSelectAllMatching:W.enableAllMatching,onClear:W.clear,children:e.jsx(A,{size:"sm",variant:"primary",onPress:()=>ue(!0),children:"Set pricing"})}):null,e.jsx(ti,{rows:fe,isLoading:xt,empty:ft,sortDescriptor:ot,onSortChange:ut,selectedKey:N,onSelect:O,onEditPricing:dt,comparisonContextTokens:st,selectedKeys:W.selectedKeys,onSelectionChange:W.onSelectionChange}),Ie?e.jsx(se,{children:e.jsxs(se.Content,{className:"p-0",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-[var(--otari-line)] px-4 py-2",children:[e.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Edit pricing"}),e.jsx(A,{size:"sm",variant:"ghost",onPress:()=>h(null),children:"Close"})]}),e.jsx(Xt,{row:Ie,onClose:()=>h(null)})]})}):null,e.jsx(Lt,{page:ke,pageSize:k,total:J,rowsOnPage:fe.length,onPageChange:j,onPageSizeChange:l=>{T(l),j(0)},pageSizeOptions:[15,25,50]})]}),ne?e.jsx("aside",{className:"lg:sticky lg:top-4",children:e.jsx(Ht,{row:ne,metadata:F[ne.key],metadataAvailable:nt,onMakeAlias:l=>t(`/aliases?target=${encodeURIComponent(l)}`),onClose:()=>O(null)})}):null]}),e.jsx(At,{isOpen:Ue,onOpenChange:ue,targetCount:We,isPending:Ge,error:Je,onSubmit:mt,title:"Set pricing",description:l=>`Apply these per-1M rates to ${l.toLocaleString()} selected ${l===1?"model":"models"}. This replaces each model's price; pricing tiers and the 1h cache rate are cleared. Edit a single model for tiers.`})]})}export{pi as ModelsPage}; +import{j as e}from"./tanstack-query-1t81HyiD.js";import{i as gt,u as vt,r as c}from"./react-dgEcD0HR.js";import{G as _t,H as bt,l as jt,J as yt,K as je,P as Pt,E as Nt,F as K,I as St,L as ae,M as Re,o as ze,N as be,O as Ct,Q as kt,S as E}from"./index-Br3lxdY5.js";import{u as Mt,r as Wt,B as It}from"./tableSelection-9hV37uhu.js";import{D as Tt}from"./DataTable-Bmn_eYSV.js";import{T as Lt,S as $t}from"./TablePagination-BR55oZ8k.js";import{B as $,d as se,f as V}from"./heroui-BI50yK5B.js";function Oe(t){const i=t.indexOf(":");return i>0?t.slice(0,i):"—"}function At(t,i=Date.now()){const r=new Map;for(const n of t){const a=r.get(n.model_key)??[];a.push(n),r.set(n.model_key,a)}const s=[];for(const n of r.values()){const a=[...n].sort((p,u)=>Date.parse(p.effective_at)-Date.parse(u.effective_at)),f=[...a].reverse().find(p=>Date.parse(p.effective_at)<=i);s.push(f??a[0])}return s.sort((n,a)=>n.model_key.localeCompare(a.model_key))}const Et="otari",De=[{value:"vision",label:"Vision",test:t=>Array.isArray(t.input_modalities)&&t.input_modalities.includes("image")},{value:"tool_call",label:"Tool calling",test:t=>!!t.tool_call},{value:"reasoning",label:"Reasoning",test:t=>!!t.reasoning},{value:"structured_output",label:"Structured output",test:t=>!!t.structured_output},{value:"attachment",label:"Attachments",test:t=>!!t.attachment},{value:"audio",label:"Audio",test:t=>Array.isArray(t.input_modalities)&&t.input_modalities.includes("audio")},{value:"pdf",label:"PDF",test:t=>Array.isArray(t.input_modalities)&&t.input_modalities.includes("pdf")}],Ot=[{key:"reasoning",label:"Reasoning"},{key:"tool_call",label:"Tool calling"},{key:"structured_output",label:"Structured output"},{key:"attachment",label:"Attachments"},{key:"temperature",label:"Temperature"}],Fe={text:"Text",image:"Image",audio:"Audio",video:"Video",pdf:"PDF"},Dt=[{value:"0",label:"Any context"},{value:"8000",label:"≥ 8K"},{value:"32000",label:"≥ 32K"},{value:"128000",label:"≥ 128K"},{value:"200000",label:"≥ 200K"},{value:"1000000",label:"≥ 1M"}],Ft=[{value:"",label:"Any price"},{value:"1",label:"≤ $1 / 1M in"},{value:"3",label:"≤ $3 / 1M in"},{value:"10",label:"≤ $10 / 1M in"},{value:"30",label:"≤ $30 / 1M in"}],Kt=[{value:"",label:"Base prices"},{value:"8000",label:"Compare at 8K"},{value:"128000",label:"Compare at 128K"},{value:"200000",label:"Compare at 200K"},{value:"500000",label:"Compare at 500K"},{value:"1000000",label:"Compare at 1M"}],Bt=[{value:"all",label:"Any release date"},{value:"365",label:"Past year"},{value:"730",label:"Past 2 years"},{value:"1095",label:"Past 3 years"}],Rt=1440*60*1e3,zt=t=>t.key;function Ke(t,i){const r=`${i}:`;return t.startsWith(r)?t.slice(r.length):t}function Be(t,i){const r={inputPrice:t.inputPrice,outputPrice:t.outputPrice,cacheReadPrice:t.cacheReadPrice,cacheWritePrice:t.cacheWritePrice,cacheWrite1hPrice:t.cacheWrite1hPrice};if(i==null)return r;const s=t.pricingTiers.filter(n=>n.min_input_tokens<=i).sort((n,a)=>a.min_input_tokens-n.min_input_tokens)[0];return s?{inputPrice:s.input_price_per_million??r.inputPrice,outputPrice:s.output_price_per_million??r.outputPrice,cacheReadPrice:s.cache_read_price_per_million??r.cacheReadPrice,cacheWritePrice:s.cache_write_price_per_million??r.cacheWritePrice,cacheWrite1hPrice:s.cache_write_1h_price_per_million??r.cacheWrite1hPrice}:r}function ce(t){const i=Number(t);return t.trim()!==""&&Number.isFinite(i)&&i>=0}function w(t){if(t.trim()==="")return!0;const i=Number(t);return Number.isFinite(i)&&i>=0}function Y(t){return t.trim()===""?null:Number(t)}function we(t){return t.map((i,r)=>({id:r,minInputTokens:String(i.min_input_tokens),input:i.input_price_per_million==null?"":String(i.input_price_per_million),output:i.output_price_per_million==null?"":String(i.output_price_per_million),cacheRead:i.cache_read_price_per_million==null?"":String(i.cache_read_price_per_million),cacheWrite:i.cache_write_price_per_million==null?"":String(i.cache_write_price_per_million),cacheWrite1h:i.cache_write_1h_price_per_million==null?"":String(i.cache_write_1h_price_per_million)}))}function Ve(t){const i=new Set;return t.every(r=>{const s=Number(r.minInputTokens),n=[r.input,r.output,r.cacheRead,r.cacheWrite,r.cacheWrite1h].some(a=>a.trim()!=="");return!Number.isInteger(s)||s<=0||i.has(s)||!n?!1:(i.add(s),[r.input,r.output,r.cacheRead,r.cacheWrite,r.cacheWrite1h].every(w))})}function Ye(t){return t.map(i=>({min_input_tokens:Number(i.minInputTokens),...i.input.trim()===""?{}:{input_price_per_million:Number(i.input)},...i.output.trim()===""?{}:{output_price_per_million:Number(i.output)},...i.cacheRead.trim()===""?{}:{cache_read_price_per_million:Number(i.cacheRead)},...i.cacheWrite.trim()===""?{}:{cache_write_price_per_million:Number(i.cacheWrite)},...i.cacheWrite1h.trim()===""?{}:{cache_write_1h_price_per_million:Number(i.cacheWrite1h)}}))}function b({value:t,onChange:i,ariaLabel:r}){return e.jsx("input",{type:"number",step:"any",min:"0",inputMode:"decimal","aria-label":r,value:t,onChange:s=>i(s.target.value),className:"w-28 rounded-md border border-[var(--otari-line)] bg-white px-2 py-1 text-right text-sm tabular-nums focus:border-[var(--otari-brand)] focus:outline-none"})}function He({tiers:t,onChange:i}){const r=(n,a,f)=>{i(t.map(p=>p.id===n?{...p,[a]:f}:p))},s=()=>{const n=t.reduce((a,f)=>Math.max(a,f.id),-1)+1;i([...t,{id:n,minInputTokens:"128000",input:"",output:"",cacheRead:"",cacheWrite:"",cacheWrite1h:""}])};return e.jsxs("div",{className:"flex flex-col gap-2 rounded-lg border border-[var(--otari-line)] p-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-xs font-medium text-[var(--otari-ink)]",children:"Long-context price tiers"}),e.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:"At a threshold, listed rates replace the base rate for the whole request."})]}),e.jsx($,{size:"sm",variant:"outline",onPress:s,children:"Add tier"})]}),t.map(n=>e.jsxs("div",{className:"flex flex-wrap items-end gap-2 border-t border-[var(--otari-line)] pt-2",children:[e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["Context ≥ tokens",e.jsx("input",{type:"number",min:"1",step:"1",inputMode:"numeric","aria-label":"Tier context threshold",value:n.minInputTokens,onChange:a=>r(n.id,"minInputTokens",a.target.value),className:"w-28 rounded-md border border-[var(--otari-line)] bg-white px-2 py-1 text-right text-sm tabular-nums focus:border-[var(--otari-brand)] focus:outline-none"})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["Input",e.jsx(b,{value:n.input,onChange:a=>r(n.id,"input",a),ariaLabel:"Tier input price"})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["Output",e.jsx(b,{value:n.output,onChange:a=>r(n.id,"output",a),ariaLabel:"Tier output price"})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["Cache read",e.jsx(b,{value:n.cacheRead,onChange:a=>r(n.id,"cacheRead",a),ariaLabel:"Tier cache read price"})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["Cache write",e.jsx(b,{value:n.cacheWrite,onChange:a=>r(n.id,"cacheWrite",a),ariaLabel:"Tier cache write price"})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-xs text-[var(--otari-muted)]",children:["1h write",e.jsx(b,{value:n.cacheWrite1h,onChange:a=>r(n.id,"cacheWrite1h",a),ariaLabel:"Tier 1 hour cache write price"})]}),e.jsx($,{size:"sm",variant:"ghost",onPress:()=>i(t.filter(a=>a.id!==n.id)),children:"Remove"})]},n.id))]})}function wt({source:t}){return t==="configured"?e.jsx(V,{size:"sm",color:"default",children:"configured"}):t==="default"||t==="alias"?e.jsx(V,{size:"sm",color:"accent",children:t}):e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"not priced"})}function oe({label:t,tone:i="info",children:r}){const s=c.useId();return e.jsxs("span",{className:"group relative inline-flex items-center font-normal normal-case",children:[e.jsx("button",{type:"button","aria-label":t,"aria-describedby":s,className:`inline-flex h-4 w-4 items-center justify-center rounded-full border text-[10px] leading-none ${i==="warning"?"border-[#c2843a] text-[#b45309]":"border-[var(--otari-line)] text-[var(--otari-muted)] hover:border-[var(--otari-brand)] hover:text-[var(--otari-brand)]"}`,children:"i"}),e.jsx("span",{id:s,role:"tooltip",className:"pointer-events-none absolute top-full right-0 z-20 mt-1.5 w-72 rounded-lg border border-[var(--otari-line)] bg-[var(--otari-surface)] px-3 py-2 text-left text-xs font-normal whitespace-normal break-words text-[var(--otari-ink)] opacity-0 shadow-lg transition-opacity group-hover:opacity-100 group-focus-within:opacity-100",children:r})]})}function Vt(){const t=kt();return t.data?t.data.default_pricing?e.jsx(oe,{label:"How unpriced models are metered",tone:"info",children:"Default pricing is on: models without a configured price are metered using community-maintained rates (the bundled genai-prices dataset). Set a price to override the fallback."}):e.jsxs(oe,{label:"How unpriced models are metered",tone:"warning",children:["Default pricing is off: only models with a configured price are metered.",t.data.require_pricing?" Requests for any other model are rejected (HTTP 402) because require_pricing is on.":" Other models are served without cost tracking."]}):null}function I({label:t,value:i}){return e.jsxs("div",{className:"flex items-baseline justify-between gap-3",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:t}),e.jsx("span",{className:"text-right text-sm text-[var(--otari-ink)] tabular-nums",children:i})]})}function le({title:t,children:i}){return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx("span",{className:"text-xs font-semibold uppercase tracking-wide text-[var(--otari-muted)]",children:t}),i]})}function Yt({row:t}){const i=je(),r=Re(),[s,n]=c.useState(!1),[a,f]=c.useState(""),[p,u]=c.useState(""),[P,j]=c.useState(""),[k,T]=c.useState(""),[y,h]=c.useState(""),[N,O]=c.useState([]),S=()=>{f(t.inputPrice==null?"":String(t.inputPrice)),u(t.outputPrice==null?"":String(t.outputPrice)),j(t.cacheReadPrice==null?"":String(t.cacheReadPrice)),T(t.cacheWritePrice==null?"":String(t.cacheWritePrice)),h(t.cacheWrite1hPrice==null?"":String(t.cacheWrite1hPrice)),O(we(t.pricingTiers)),n(!0)},X=ce(a)&&ce(p)&&w(P)&&w(k)&&w(y)&&Ve(N),W=()=>{X&&i.mutate({model_key:t.key,input_price_per_million:Number(a),output_price_per_million:Number(p),cache_read_price_per_million:Y(P),cache_write_price_per_million:Y(k),cache_write_1h_price_per_million:Y(y),pricing_tiers:Ye(N)},{onSuccess:()=>n(!1)})};return s?e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Input $ / 1M"}),e.jsx(b,{value:a,onChange:f,ariaLabel:`Input price for ${t.key}`})]}),e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Output $ / 1M"}),e.jsx(b,{value:p,onChange:u,ariaLabel:`Output price for ${t.key}`})]}),e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Cache read $ / 1M"}),e.jsx(b,{value:P,onChange:j,ariaLabel:`Cache read price for ${t.key}`})]}),e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Cache write $ / 1M"}),e.jsx(b,{value:k,onChange:T,ariaLabel:`Cache write price for ${t.key}`})]}),e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"1h cache write $ / 1M"}),e.jsx(b,{value:y,onChange:h,ariaLabel:`1 hour cache write price for ${t.key}`})]}),e.jsx(He,{tiers:N,onChange:O}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx($,{size:"sm",variant:"primary",isDisabled:i.isPending||!X,onPress:W,children:"Save"}),e.jsx($,{size:"sm",variant:"ghost",isDisabled:i.isPending,onPress:()=>n(!1),children:"Cancel"})]}),i.error?e.jsx("span",{className:"text-xs text-red-700",children:be(i.error)}):null]}):e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(I,{label:"Input",value:t.inputPrice==null?"—":`${E(t.inputPrice)} / 1M`}),e.jsx(I,{label:"Output",value:t.outputPrice==null?"—":`${E(t.outputPrice)} / 1M`}),e.jsx(I,{label:"Cache read",value:t.cacheReadPrice==null?"—":`${E(t.cacheReadPrice)} / 1M`}),e.jsx(I,{label:"Cache write",value:t.cacheWritePrice==null?"—":`${E(t.cacheWritePrice)} / 1M`}),e.jsx(I,{label:"1h cache write",value:t.cacheWrite1hPrice==null?"—":`${E(t.cacheWrite1hPrice)} / 1M`}),e.jsx(I,{label:"Context tiers",value:t.pricingTiers.length?`${t.pricingTiers.length} configured`:"—"}),e.jsxs("div",{className:"flex items-center gap-2 pt-1",children:[e.jsx($,{size:"sm",variant:"outline",onPress:S,children:t.source==="configured"?"Edit price":"Set price"}),t.source==="configured"?e.jsxs(e.Fragment,{children:[e.jsx(ze,{confirmLabel:"Reset",isPending:r.isPending,onConfirm:()=>r.mutate(t.key),children:"Reset"}),e.jsx(oe,{label:"What reset does",children:"Removes the custom price. The model reverts to the default rate (genai-prices) when default pricing is on, otherwise it is metered at no cost."})]}):null,r.error?e.jsx("span",{className:"text-xs text-red-700",children:be(r.error)}):null]})]})}function Ht({row:t,metadata:i,metadataAvailable:r,onMakeAlias:s,onClose:n}){const a=(i==null?void 0:i.input_modalities)??[],f=(i==null?void 0:i.output_modalities)??[],p=Ot.filter(({key:u})=>i==null?void 0:i[u]);return e.jsx(se,{children:e.jsxs(se.Content,{className:"flex flex-col gap-5 p-5",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h2",{className:"text-base font-semibold break-all text-[var(--otari-ink)]",children:t.model}),i!=null&&i.deprecated?e.jsx(V,{size:"sm",color:"danger",children:"deprecated"}):null]}),e.jsxs("p",{className:"mt-1 text-xs break-all text-[var(--otari-muted)]",children:["Selector: ",e.jsx("code",{children:t.key})]}),i!=null&&i.family?e.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:i.family}):null]}),e.jsx("button",{type:"button","aria-label":"Close model details",onClick:n,className:"-mt-1 -mr-1 shrink-0 rounded-md px-1.5 py-0.5 text-lg leading-none text-[var(--otari-muted)] hover:bg-[var(--otari-bg)] hover:text-[var(--otari-ink)]",children:"✕"})]}),i!=null&&i.description?e.jsx("p",{className:"text-sm text-[var(--otari-ink)]",children:i.description}):null,e.jsxs(le,{title:"Pricing",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(wt,{source:t.source}),t.isDiscovered?null:e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"not discovered"})]}),e.jsx(Yt,{row:t},t.key),e.jsx($,{size:"sm",variant:"outline",onPress:()=>s(t.key),children:"Make an alias"})]}),e.jsxs(le,{title:"Specs",children:[e.jsx(I,{label:"Context window",value:ae(t.contextWindow)}),e.jsx(I,{label:"Max output",value:ae((i==null?void 0:i.max_output_tokens)??null)}),e.jsx(I,{label:"Knowledge cutoff",value:(i==null?void 0:i.knowledge_cutoff)??"—"}),e.jsx(I,{label:"Released",value:Ct(i==null?void 0:i.release_date)}),e.jsx(I,{label:"Open weights",value:i?i.open_weights?"Yes":"No":"—"})]}),e.jsx(le,{title:"Modalities",children:a.length===0&&f.length===0?e.jsx("span",{className:"text-sm text-[var(--otari-muted)]",children:"Unknown."}):e.jsxs("div",{className:"flex flex-col gap-1.5 text-xs text-[var(--otari-muted)]",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-1",children:[e.jsx("span",{children:"In:"}),a.map(u=>e.jsx(V,{size:"sm",color:"default",children:Fe[u]??u},u))]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-1",children:[e.jsx("span",{children:"Out:"}),f.map(u=>e.jsx(V,{size:"sm",color:"default",children:Fe[u]??u},u))]})]})}),e.jsx(le,{title:"Capabilities",children:p.length>0?e.jsx("div",{className:"flex flex-wrap gap-1.5",children:p.map(({key:u,label:P})=>e.jsx(V,{size:"sm",color:"default",children:P},u))}):e.jsx("span",{className:"text-sm text-[var(--otari-muted)]",children:r?"None reported.":"Extended metadata unavailable (models.dev disabled or unreachable)."})})]})})}const qt=15;function Ut({value:t,onChange:i,placeholder:r}){return e.jsx("input",{type:"search",value:t,onChange:s=>i(s.target.value),placeholder:r,"aria-label":r,className:"w-full max-w-xs rounded-md border border-[var(--otari-line)] bg-white px-3 py-1.5 text-sm focus:border-[var(--otari-brand)] focus:outline-none"})}const qe="otari.dashboard.modelsSort",_e={col:"model",dir:"asc"},Gt=["model","released","input","output"];function Jt(){if(typeof window>"u")return _e;try{const t=window.localStorage.getItem(qe);if(!t)return _e;const i=JSON.parse(t);if(Gt.includes(i.col)&&(i.dir==="asc"||i.dir==="desc"))return{col:i.col,dir:i.dir}}catch{}return _e}function Qt({row:t,onClose:i}){const r=je(),s=Re(),[n,a]=c.useState(t.inputPrice==null?"":String(t.inputPrice)),[f,p]=c.useState(t.outputPrice==null?"":String(t.outputPrice)),[u,P]=c.useState(t.cacheReadPrice==null?"":String(t.cacheReadPrice)),[j,k]=c.useState(t.cacheWritePrice==null?"":String(t.cacheWritePrice)),[T,y]=c.useState(t.cacheWrite1hPrice==null?"":String(t.cacheWrite1hPrice)),[h,N]=c.useState(we(t.pricingTiers)),O=ce(n)&&ce(f)&&w(u)&&w(j)&&w(T)&&Ve(h),S=()=>{O&&r.mutate({model_key:t.key,input_price_per_million:Number(n),output_price_per_million:Number(f),cache_read_price_per_million:Y(u),cache_write_price_per_million:Y(j),cache_write_1h_price_per_million:Y(T),pricing_tiers:Ye(h)},{onSuccess:i})};return e.jsxs("div",{className:"flex flex-col gap-3 px-4 py-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("span",{className:"text-xs font-medium break-all text-[var(--otari-muted)]",children:t.key}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:["Input $ / 1M",e.jsx(b,{value:n,onChange:a,ariaLabel:`Input price for ${t.key}`})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:["Output $ / 1M",e.jsx(b,{value:f,onChange:p,ariaLabel:`Output price for ${t.key}`})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:["Cache read $ / 1M",e.jsx(b,{value:u,onChange:P,ariaLabel:`Cache read price for ${t.key}`})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:["Cache write $ / 1M",e.jsx(b,{value:j,onChange:k,ariaLabel:`Cache write price for ${t.key}`})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:["1h cache write $ / 1M",e.jsx(b,{value:T,onChange:y,ariaLabel:`1 hour cache write price for ${t.key}`})]}),e.jsx($,{size:"sm",variant:"primary",isDisabled:r.isPending||!O,onPress:S,children:r.isPending?"Saving…":"Save"}),e.jsx($,{size:"sm",variant:"ghost",isDisabled:r.isPending,onPress:i,children:"Cancel"}),t.source==="configured"?e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(ze,{confirmLabel:"Reset",isPending:s.isPending,onConfirm:()=>s.mutate(t.key,{onSuccess:i}),children:"Reset"}),e.jsx(oe,{label:"What reset does",children:"Removes the custom price. The model reverts to the default rate (genai-prices) when default pricing is on, otherwise it is metered at no cost."})]}):null,r.error||s.error?e.jsx("span",{className:"text-xs text-red-700",children:be(r.error??s.error)}):null]}),e.jsx(He,{tiers:h,onChange:N})]})}function Xt({primary:t,secondary:i,rowKey:r,primaryLabel:s,secondaryLabel:n,onEdit:a}){const f=(p,u)=>e.jsx("button",{type:"button","aria-label":`Edit ${u} price for ${r}`,className:"tabular-nums hover:text-[var(--otari-brand-dark)] hover:underline",onClick:P=>{P.stopPropagation(),a()},children:p==null?"—":E(p)});return e.jsxs("span",{className:"inline-flex items-center justify-end gap-1",children:[f(t,s),e.jsx("span",{className:"text-[var(--otari-muted)]",children:"/"}),f(i,n)]})}function Zt({rates:t,rowKey:i,onEdit:r}){const s=[t.cacheReadPrice==null?null:`R ${E(t.cacheReadPrice)}`,t.cacheWritePrice==null?null:`W ${E(t.cacheWritePrice)}`,t.cacheWrite1hPrice==null?null:`1h ${E(t.cacheWrite1hPrice)}`].filter(n=>n!==null);return e.jsx("button",{type:"button","aria-label":`Edit caching price for ${i}`,className:"max-w-44 text-right text-xs leading-5 text-[var(--otari-muted)] hover:text-[var(--otari-brand-dark)] hover:underline",onClick:n=>{n.stopPropagation(),r()},children:s.length>0?s.join(" · "):"Input-rate fallback"})}function ei({row:t,onEdit:i}){const r=[...t.pricingTiers].sort((n,a)=>n.min_input_tokens-a.min_input_tokens).map(n=>ae(n.min_input_tokens)),s=r.length===0?"Base only":`${r.length} tier${r.length===1?"":"s"} · ≥ ${r.join(", ")}`;return e.jsx("button",{type:"button","aria-label":`Edit pricing policy for ${t.key}`,className:"max-w-40 text-right text-xs leading-5 text-[var(--otari-muted)] hover:text-[var(--otari-brand-dark)] hover:underline",onClick:n=>{n.stopPropagation(),i()},children:s})}function ti({rows:t,isLoading:i,empty:r,sortDescriptor:s,onSortChange:n,selectedKey:a,onSelect:f,onEditPricing:p,comparisonContextTokens:u,selectedKeys:P,onSelectionChange:j}){const k=c.useMemo(()=>{const y=u==null?"Base":`at ${ae(u)}`;return[{id:"model",header:"Model",isRowHeader:!0,allowsSorting:!0,cell:h=>e.jsxs("span",{className:"font-medium break-all",children:[h.model,e.jsx("span",{className:"sr-only",children:h.key})]})},{id:"provider",header:"Provider",cell:h=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:h.provider})},{id:"input",header:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[`${y} in / out $ / 1M`,e.jsx(Vt,{})]}),align:"end",allowsSorting:!0,cell:h=>{const N=Be(h,u);return e.jsx(Xt,{primary:N.inputPrice,secondary:N.outputPrice,rowKey:h.key,primaryLabel:"input",secondaryLabel:"output",onEdit:()=>p(h.key)})}},{id:"caching",header:`Caching ${u==null?"policy":y}`,align:"end",cell:h=>e.jsx(Zt,{rates:Be(h,u),rowKey:h.key,onEdit:()=>p(h.key)})},{id:"policy",header:"Pricing policy",align:"end",cell:h=>e.jsx(ei,{row:h,onEdit:()=>p(h.key)})}]},[u,p]),T=c.useCallback(y=>y.key===a?"bg-[var(--otari-brand-tint)]":void 0,[a]);return e.jsx(Tt,{ariaLabel:"Models",columns:k,rows:t,getRowKey:zt,isLoading:i,emptyContent:r,selectionMode:"multiple",selectedKeys:P,onSelectionChange:j,sortDescriptor:s,onSortChange:n,onRowAction:f,rowClassName:T})}function ii({providers:t}){return t.length===0?null:e.jsxs(St,{tone:"warning",children:["Could not list ",t.map(i=>i.provider).join(", "),". Check that provider's credentials in config.yml; its models are missing from the list below."]})}function pi(){var Te,Le,$e;const t=gt(),[i]=vt(),r=_t(),s=bt(),n=jt(),a=yt(),f=je(),[p,u]=c.useState(""),[P,j]=c.useState(0),[k,T]=c.useState(qt),[y,h]=c.useState(null),[N,O]=c.useState(null),[S,X]=c.useState(Jt),W=Mt(),[Ue,ue]=c.useState(!1),[Ge,ye]=c.useState(!1),[Je,Pe]=c.useState(void 0);c.useEffect(()=>{try{window.localStorage.setItem(qe,JSON.stringify(S))}catch{}},[S]);const[D,Ne]=c.useState(i.get("provider")||"all"),[B,Qe]=c.useState("all"),[H,Xe]=c.useState("all"),[q,Ze]=c.useState("all"),[de,et]=c.useState("0"),[Z,tt]=c.useState(""),[ee,it]=c.useState("all"),[pe,rt]=c.useState(""),F=((Te=a.data)==null?void 0:Te.models)??{},nt=((Le=a.data)==null?void 0:Le.available)??!1,Se=c.useMemo(()=>{var l;return new Set((((l=n.data)==null?void 0:l.providers)??[]).flatMap(m=>m.models.map(o=>o.key)))},[n.data]),lt=l=>{u(l),j(0)},R=l=>m=>{l(m),j(0)},te=c.useMemo(()=>{var M,v,_,A,L,z,Ae;const l=new Map(At(s.data??[]).map(d=>[d.model_key,d])),m=[],o=new Set,g=(d,ve,Ee,x)=>{if(o.has(d))return;o.add(d);const C=l.get(d);m.push({key:d,model:Ke(ve,Ee),provider:Ee,isDiscovered:Se.has(d),contextWindow:(x==null?void 0:x.contextWindow)??null,inputPrice:C?C.input_price_per_million:(x==null?void 0:x.inputPrice)??null,outputPrice:C?C.output_price_per_million:(x==null?void 0:x.outputPrice)??null,cacheReadPrice:C?C.cache_read_price_per_million:(x==null?void 0:x.cacheReadPrice)??null,cacheWritePrice:C?C.cache_write_price_per_million:(x==null?void 0:x.cacheWritePrice)??null,cacheWrite1hPrice:C?C.cache_write_1h_price_per_million??null:(x==null?void 0:x.cacheWrite1hPrice)??null,pricingTiers:C?C.pricing_tiers??[]:(x==null?void 0:x.pricingTiers)??[],source:C?"configured":(x==null?void 0:x.source)??"none"})};for(const d of((M=r.data)==null?void 0:M.data)??[]){if(d.owned_by===Et)continue;const ve=d.pricing_source==="default"?"default":d.pricing?"configured":"none";g(d.id,d.id,d.owned_by||Oe(d.id),{key:d.id,model:d.id,provider:d.owned_by,contextWindow:d.context_window,inputPrice:((v=d.pricing)==null?void 0:v.input_price_per_million)??null,outputPrice:((_=d.pricing)==null?void 0:_.output_price_per_million)??null,cacheReadPrice:((A=d.pricing)==null?void 0:A.cache_read_price_per_million)??null,cacheWritePrice:((L=d.pricing)==null?void 0:L.cache_write_price_per_million)??null,cacheWrite1hPrice:((z=d.pricing)==null?void 0:z.cache_write_1h_price_per_million)??null,pricingTiers:((Ae=d.pricing)==null?void 0:Ae.pricing_tiers)??[],source:ve})}for(const d of l.keys())g(d,d,Oe(d));return m},[r.data,s.data,Se]),Ce=c.useMemo(()=>new Map(te.map(l=>[l.key,l])),[te]),ie=c.useMemo(()=>{var o,g,M;const l=te.map(v=>{var _,A;return{...v,contextWindow:v.contextWindow??((_=F[v.key])==null?void 0:_.context_window)??null,releaseDate:((A=F[v.key])==null?void 0:A.release_date)??null}}),m=new Set(l.map(v=>v.key));for(const v of((o=n.data)==null?void 0:o.providers)??[])for(const _ of v.models)m.has(_.key)||(m.add(_.key),l.push({key:_.key,model:Ke(_.key,v.provider),provider:v.provider,isDiscovered:!0,contextWindow:((g=F[_.key])==null?void 0:g.context_window)??null,releaseDate:((M=F[_.key])==null?void 0:M.release_date)??null,inputPrice:null,outputPrice:null,cacheReadPrice:null,cacheWritePrice:null,cacheWrite1hPrice:null,pricingTiers:[],source:"none"}));return l},[te,n.data,F]),at=((($e=n.data)==null?void 0:$e.providers)??[]).filter(l=>!l.ok),re=c.useMemo(()=>{const l=Array.from(new Set(ie.map(m=>m.provider))).sort((m,o)=>m.localeCompare(o));return[{value:"all",label:"All providers"},...l.map(m=>({value:m,label:m}))]},[ie]);c.useEffect(()=>{D==="all"||re.length<=1||re.some(l=>l.value===D)||Ne("all")},[re,D]);const U=p.trim().toLowerCase(),he=Number(de)||0,me=Z===""?Number.POSITIVE_INFINITY:Number(Z),st=pe===""?null:Number(pe),xe=ee==="all"?null:Date.now()-Number(ee)*Rt,G=c.useMemo(()=>{const l=o=>{if(U&&!o.key.toLowerCase().includes(U)&&!o.provider.toLowerCase().includes(U)||D!=="all"&&o.provider!==D||B==="configured"&&o.source!=="configured"||B==="default"&&o.source!=="default"||B==="priced"&&o.inputPrice==null||B==="unpriced"&&o.inputPrice!=null||H==="discovered"&&!o.isDiscovered||H==="custom"&&o.isDiscovered)return!1;if(q!=="all"){const g=De.find(v=>v.value===q),M=F[o.key];if(!g||!M||!g.test(M))return!1}if(he>0&&(o.contextWindow==null||o.contextWindowme))return!1;if(xe!=null){const g=o.releaseDate?Date.parse(o.releaseDate):Number.NaN;if(Number.isNaN(g)||g{const M=S.dir==="asc"?1:-1;if(S.col==="model")return o.model.localeCompare(g.model)*M;if(S.col==="released"){const L=o.releaseDate??null,z=g.releaseDate??null;return!L&&!z?o.model.localeCompare(g.model):L?z?(Lz?1:0)*M||o.model.localeCompare(g.model):-1:1}const v=L=>S.col==="input"?L.inputPrice:L.outputPrice,_=v(o),A=v(g);return _==null&&A==null?o.model.localeCompare(g.model):_==null?1:A==null?-1:(_-A)*M||o.model.localeCompare(g.model)};return ie.filter(l).sort(m)},[ie,U,D,B,H,q,he,me,xe,F,S]),J=G.length,ct=Math.max(1,Math.ceil(J/k)),ke=Math.min(P,ct-1),Me=ke*k,fe=G.slice(Me,Me+k),ot={column:S.col,direction:S.dir==="asc"?"ascending":"descending"},ut=l=>{X({col:String(l.column),dir:l.direction==="ascending"?"asc":"desc"}),j(0)},dt=c.useCallback(l=>h(m=>m===l?null:l),[]),ge=fe.map(l=>l.key),Q=Wt(W.selectedKeys,ge),pt=ge.length>0&&Q.length===ge.length&&J>Q.length,ht=W.allMatching?G.map(l=>l.key):Q,We=W.allMatching?J:Q.length,Ie=y?G.find(l=>l.key===y)??Ce.get(y)??null:null,mt=async l=>{ye(!0),Pe(void 0);try{for(const m of ht)await f.mutateAsync({model_key:m,input_price_per_million:l.input_price_per_million,output_price_per_million:l.output_price_per_million,cache_read_price_per_million:l.cache_read_price_per_million??null,cache_write_price_per_million:l.cache_write_price_per_million??null,cache_write_1h_price_per_million:null,pricing_tiers:[]});W.clear(),ue(!1)}catch(m){Pe(m)}finally{ye(!1)}},xt=r.isLoading||s.isLoading||n.isLoading,ft=U!==""||D!=="all"||B!=="all"||H!=="all"||q!=="all"||de!=="0"||Z!==""||ee!=="all"?"No models match your filters.":"No models yet. Add a provider on the Providers page, or price a model below.",ne=N?G.find(l=>l.key===N)??Ce.get(N)??null:null;return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(Pt,{title:"Models",description:"Every model your providers can serve. Set a price on any model so budgets and usage tracking work."}),e.jsx(Nt,{error:r.error??s.error??n.error??a.error}),e.jsxs("div",{className:`grid gap-4 lg:items-start ${ne?"lg:grid-cols-[minmax(0,1fr)_360px]":"grid-cols-1"}`,children:[e.jsxs("div",{className:"flex min-w-0 flex-col gap-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx(Ut,{value:p,onChange:lt,placeholder:"Search models…"}),e.jsx(K,{ariaLabel:"Filter by provider",value:D,onChange:R(Ne),options:re}),e.jsx(K,{ariaLabel:"Filter by pricing",value:B,onChange:R(Qe),options:[{value:"all",label:"Any pricing"},{value:"configured",label:"Custom price"},{value:"default",label:"Default price"},{value:"priced",label:"Priced"},{value:"unpriced",label:"Unpriced"}]}),e.jsx(K,{ariaLabel:"Filter by source",value:H,onChange:R(Xe),options:[{value:"all",label:"Any source"},{value:"discovered",label:"Discovered"},{value:"custom",label:"Custom (not discovered)"}]}),e.jsx(K,{ariaLabel:"Filter by capability",value:q,onChange:R(Ze),options:[{value:"all",label:"Any capability"},...De.map(l=>({value:l.value,label:l.label}))]}),e.jsx(K,{ariaLabel:"Minimum context window",value:de,onChange:R(et),options:Dt}),e.jsx(K,{ariaLabel:"Maximum input price",value:Z,onChange:R(tt),options:Ft}),e.jsx(K,{ariaLabel:"Compare prices at context",value:pe,onChange:rt,options:Kt}),e.jsx(K,{ariaLabel:"Filter by release date",value:ee,onChange:R(it),options:Bt})]}),e.jsx(ii,{providers:at}),Q.length>0?e.jsx(It,{selectedCount:We,allMatching:W.allMatching,matchingTotal:J,canSelectAllMatching:pt,onSelectAllMatching:W.enableAllMatching,onClear:W.clear,children:e.jsx($,{size:"sm",variant:"primary",onPress:()=>ue(!0),children:"Set pricing"})}):null,e.jsx(ti,{rows:fe,isLoading:xt,empty:ft,sortDescriptor:ot,onSortChange:ut,selectedKey:N,onSelect:O,onEditPricing:dt,comparisonContextTokens:st,selectedKeys:W.selectedKeys,onSelectionChange:W.onSelectionChange}),Ie?e.jsx(se,{children:e.jsxs(se.Content,{className:"p-0",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-[var(--otari-line)] px-4 py-2",children:[e.jsx("span",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Edit pricing"}),e.jsx($,{size:"sm",variant:"ghost",onPress:()=>h(null),children:"Close"})]}),e.jsx(Qt,{row:Ie,onClose:()=>h(null)})]})}):null,e.jsx(Lt,{page:ke,pageSize:k,total:J,rowsOnPage:fe.length,onPageChange:j,onPageSizeChange:l=>{T(l),j(0)},pageSizeOptions:[15,25,50]})]}),ne?e.jsx("aside",{className:"lg:sticky lg:top-4",children:e.jsx(Ht,{row:ne,metadata:F[ne.key],metadataAvailable:nt,onMakeAlias:l=>t(`/aliases?target=${encodeURIComponent(l)}`),onClose:()=>O(null)})}):null]}),e.jsx($t,{isOpen:Ue,onOpenChange:ue,targetCount:We,isPending:Ge,error:Je,onSubmit:mt,title:"Set pricing",description:l=>`Apply these per-1M rates to ${l.toLocaleString()} selected ${l===1?"model":"models"}. This replaces each model's price; pricing tiers and the 1h cache rate are cleared. Edit a single model for tiers.`})]})}export{pi as ModelsPage}; diff --git a/src/gateway/static/dashboard/assets/OverviewPage-BedTU_aZ.js b/src/gateway/static/dashboard/assets/OverviewPage-BedTU_aZ.js deleted file mode 100644 index 7957de38..00000000 --- a/src/gateway/static/dashboard/assets/OverviewPage-BedTU_aZ.js +++ /dev/null @@ -1 +0,0 @@ -import{j as t}from"./tanstack-query-1t81HyiD.js";import{r as b,i as rt,N as G}from"./react-dgEcD0HR.js";import{L as at,M as st,d as j,N as nt,l as ot,a as it,u as lt,b as dt,O as w,P as ct,R as ut,E as I,S as g,Q as _,T as N,U as R,V as D,W as mt}from"./index-DV8qvdUj.js";import{S as q}from"./charts-DIsd9m--.js";import{D as vt}from"./DataTable-Bmn_eYSV.js";import{d as H,B as ht}from"./heroui-BI50yK5B.js";import"./recharts-C4kRDmvS.js";function M(e){return e==="neutral"?void 0:e}const xt=.02,ft=.1;function B(e){if(!e||e.request_count===0)return{rate:null,status:"neutral"};const i=e.error_count/e.request_count,a=i>=ft?"alert":i>=xt?"warn":"ok";return{rate:i,status:a}}function gt(e){return!e||e.total===0?"neutral":e.healthy>=e.total?"ok":e.healthy===0?"alert":"warn"}const pt=.8;function bt(e){if(e.length===0)return{status:"neutral",label:"No budgets configured",overCount:0,nearCount:0,cappedCount:0};const i=e.filter(r=>r.max_budget!==null&&r.user_count>0);if(i.length===0)return{status:"neutral",label:"No capped budgets",overCount:0,nearCount:0,cappedCount:0};let a=0,s=0,n,c=-1;for(const r of i){const o=r.max_budget*r.user_count,l=o>0?r.total_spend/o:0;l>=1?a+=1:l>=pt&&(s+=1),l>c&&(c=l,n={name:r.name??r.budget_id,spent:r.total_spend,allocated:o,pct:l})}const d=a>0?"alert":s>0?"warn":"ok",p=a>0?`${a} over limit`:s>0?`${s} near limit`:"All within budget";return{status:d,label:p,overCount:a,nearCount:s,cappedCount:i.length,worst:n}}const W=864e5,K=30;function T(){const e=new Date;return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`}function yt(){const[e,i]=b.useState(T);return b.useEffect(()=>{const a=()=>{if(document.visibilityState==="visible"){const s=T();i(n=>n===s?n:s)}};return document.addEventListener("visibilitychange",a),window.addEventListener("focus",a),()=>{document.removeEventListener("visibilitychange",a),window.removeEventListener("focus",a)}},[]),b.useMemo(()=>{const a=Date.now(),s=new Date(a);return{today:new Date(s.getFullYear(),s.getMonth(),s.getDate()).toISOString(),periodStart:new Date(a-K*W).toISOString(),prevStart:new Date(a-2*K*W).toISOString()}},[e])}const St={ok:"Healthy",warn:"Elevated",alert:"High"},jt={ok:"On track",warn:"Near limit",alert:"Over budget"};function Pt(){const e=at();return e.isLoading?t.jsx(st,{}):t.jsx(wt,{needsSetup:e.isSuccess&&e.data.providers.length===0})}function wt({needsSetup:e=!1}){var L,O,A,$,F,P,U;const i=yt(),a=b.useMemo(()=>({start_date:i.today}),[i]),s=b.useMemo(()=>({start_date:i.periodStart}),[i]),n=b.useMemo(()=>({start_date:i.prevStart,end_date:i.periodStart}),[i]),c=j(a,"hour"),d=j(s,"day"),p=j(n,"day"),r=nt(),o=ot(),l=it(),y=lt(),h=dt({},0,5),E=(L=c.data)==null?void 0:L.totals,v=(O=d.data)==null?void 0:O.totals,x=(A=p.data)==null?void 0:A.totals,S=(($=d.data)==null?void 0:$.series)??[],C=S.length>1,u=B(v),k=B(x),Y=u.rate!==null&&k.rate!==null?w(u.rate,k.rate):null,m=bt(o.data??[]),V=gt(r.data),Q=(l.data??[]).filter(f=>f.is_active).length,z=(y.data??[]).filter(f=>!f.blocked).length,J=(((F=h.data)==null?void 0:F.length)??0)>0,X=e&&h.isSuccess&&!J,Z=c.error??d.error??r.error??o.error??l.error??y.error,tt=()=>{c.refetch(),d.refetch(),p.refetch(),r.refetch(),o.refetch(),l.refetch(),y.refetch(),h.refetch()},et=c.isFetching||d.isFetching||r.isFetching||o.isFetching||l.isFetching||y.isFetching||h.isFetching;return t.jsxs("div",{className:"flex flex-col gap-6",children:[t.jsx(ct,{title:"Overview",description:"At-a-glance spend, traffic, and health across the gateway.",action:t.jsx(ut,{onRefresh:tt,isFetching:et,updatedAt:d.dataUpdatedAt})}),X?t.jsx(Nt,{}):null,t.jsx(I,{error:Z}),t.jsx(_t,{providerHealth:V,healthy:((P=r.data)==null?void 0:P.healthy)??0,total:((U=r.data)==null?void 0:U.total)??0,budget:m,errStatus:u.status,errRate:u.rate,ready:r.isSuccess&&o.isSuccess&&d.isSuccess,failed:r.isError||o.isError||d.isError}),t.jsxs("div",{className:"grid grid-cols-2 gap-4 sm:grid-cols-3 xl:grid-cols-4",children:[t.jsx(g,{label:"Spend today",value:E?_(E.cost):"—"}),t.jsx(g,{label:"Spend, last 30 days",value:v?_(v.cost):"—",hint:v?t.jsx(N,{fraction:w(v.cost,x==null?void 0:x.cost)}):null,chart:C?t.jsx(q,{values:S.map(f=>f.cost),ariaLabel:"Spend trend over the last 30 days"}):void 0}),t.jsx(g,{label:"Requests, last 30 days",value:v?R(v.request_count):"—",hint:v?t.jsx(N,{fraction:w(v.request_count,x==null?void 0:x.request_count)}):null,chart:C?t.jsx(q,{values:S.map(f=>f.requests),ariaLabel:"Request volume trend over the last 30 days"}):void 0}),t.jsx(g,{label:"Error rate, last 30 days",value:u.rate===null?"—":D(u.rate),status:M(u.status),statusLabel:u.status==="neutral"?void 0:St[u.status],hint:u.rate!==null?t.jsx(N,{fraction:Y}):null}),t.jsx(g,{label:"Budget health",value:o.data&&m.worst?D(m.worst.pct):"—",status:o.data?M(m.status):void 0,statusLabel:o.data&&m.status!=="neutral"?jt[m.status]:void 0,hint:o.data?m.worst?`${m.label} · worst: ${m.worst.name}`:m.label:void 0,to:"/budgets"}),t.jsx(g,{label:"Active keys",value:l.data?R(Q):"—",to:"/keys"}),t.jsx(g,{label:"Active users",value:y.data?R(z):"—",to:"/users"})]}),t.jsx(Et,{entries:h.data??[],loading:h.isLoading,error:h.error})]})}function Nt(){const e=rt();return t.jsx(H,{children:t.jsxs(H.Content,{className:"flex flex-col gap-3 p-6",children:[t.jsxs("div",{children:[t.jsx("h2",{className:"text-lg font-semibold text-[var(--otari-ink)]",children:"Get started with Otari"}),t.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:"Add a provider to begin serving models. Once it is configured, this page will show your gateway’s traffic, spend, and health."})]}),t.jsx("div",{children:t.jsx(ht,{variant:"primary",onPress:()=>e("/providers"),children:"Add your first provider"})})]})})}function Rt({text:e}){return t.jsx("div",{role:"status",className:"flex items-center gap-2 rounded-xl border border-[var(--otari-line)] bg-[var(--otari-bg)] px-4 py-3 text-sm text-[var(--otari-muted)]",children:e})}function _t({providerHealth:e,healthy:i,total:a,budget:s,errStatus:n,errRate:c,ready:d,failed:p}){if(p)return t.jsx(Rt,{text:"Some status data could not be loaded."});if(!d)return null;const r=[];if((e==="warn"||e==="alert")&&a>0){const o=a-i;r.push({text:`${o} provider${o===1?"":"s"} unreachable`,to:"/providers"})}return s.overCount>0?r.push({text:`${s.overCount} budget${s.overCount===1?"":"s"} over limit`,to:"/budgets"}):s.nearCount>0&&r.push({text:`${s.nearCount} budget${s.nearCount===1?"":"s"} near limit`,to:"/budgets"}),n==="alert"&&c!==null&&r.push({text:`error rate ${D(c)}`,to:"/activity?status=error"}),r.length===0?null:t.jsxs("div",{role:"alert",className:"flex flex-col gap-2 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-900 sm:flex-row sm:flex-wrap sm:items-center",children:[t.jsx("span",{className:"font-medium",children:"Needs attention:"}),r.map((o,l)=>t.jsxs("span",{className:"flex items-center gap-2",children:[l>0?t.jsx("span",{"aria-hidden":!0,className:"text-amber-400",children:"·"}):null,t.jsx(G,{to:o.to,className:"underline underline-offset-2 hover:text-amber-950",children:o.text})]},o.to+o.text))]})}function Dt(e){return e==="error"?"error":"ok"}function Et({entries:e,loading:i,error:a}){const s=[{id:"time",header:"Time",cell:n=>t.jsx("span",{className:"text-[var(--otari-muted)]",title:new Date(n.timestamp).toLocaleString(),children:mt(n.timestamp)})},{id:"model",header:"Model",isRowHeader:!0,cell:n=>t.jsx("span",{className:"text-[var(--otari-ink)]",children:n.model})},{id:"cost",header:"Cost",align:"end",cell:n=>n.cost===null?"—":_(n.cost)},{id:"status",header:"Status",cell:n=>t.jsx("span",{className:`inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium ${n.status==="error"?"border-red-200 bg-red-50 text-red-700":"border-[var(--otari-line)] bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]"}`,children:Dt(n.status)})}];return t.jsxs("div",{className:"flex flex-col gap-3",children:[t.jsxs("div",{className:"flex items-center justify-between",children:[t.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Recent activity"}),t.jsx(G,{to:"/activity",className:"text-sm text-[var(--otari-brand-dark)] hover:underline",children:"View all →"})]}),t.jsx(I,{error:a}),t.jsx(vt,{ariaLabel:"Recent activity",columns:s,rows:e,getRowKey:n=>n.id,isLoading:i,emptyContent:"No requests yet. Once the gateway serves traffic, it appears here."})]})}export{Pt as OverviewIndex,wt as OverviewPage,T as localDayKey}; diff --git a/src/gateway/static/dashboard/assets/OverviewPage-DPI1n-nt.js b/src/gateway/static/dashboard/assets/OverviewPage-DPI1n-nt.js new file mode 100644 index 00000000..d5adebbe --- /dev/null +++ b/src/gateway/static/dashboard/assets/OverviewPage-DPI1n-nt.js @@ -0,0 +1 @@ +import{j as t}from"./tanstack-query-1t81HyiD.js";import{r as b,i as rt,N as G}from"./react-dgEcD0HR.js";import{T as at,U as st,d as S,V as nt,q as ot,a as it,u as lt,b as dt,W as w,P as ct,R as ut,E as I,X as g,Y as _,Z as N,_ as R,$ as D,a0 as mt}from"./index-Br3lxdY5.js";import{S as U}from"./charts-DIsd9m--.js";import{D as vt}from"./DataTable-Bmn_eYSV.js";import{d as H,B as ht}from"./heroui-BI50yK5B.js";import"./recharts-C4kRDmvS.js";function B(e){return e==="neutral"?void 0:e}const xt=.02,ft=.1;function M(e){if(!e||e.request_count===0)return{rate:null,status:"neutral"};const i=e.error_count/e.request_count,a=i>=ft?"alert":i>=xt?"warn":"ok";return{rate:i,status:a}}function gt(e){return!e||e.total===0?"neutral":e.healthy>=e.total?"ok":e.healthy===0?"alert":"warn"}const pt=.8;function bt(e){if(e.length===0)return{status:"neutral",label:"No budgets configured",overCount:0,nearCount:0,cappedCount:0};const i=e.filter(r=>r.max_budget!==null&&r.user_count>0);if(i.length===0)return{status:"neutral",label:"No capped budgets",overCount:0,nearCount:0,cappedCount:0};let a=0,s=0,n,c=-1;for(const r of i){const o=r.max_budget*r.user_count,l=o>0?r.total_spend/o:0;l>=1?a+=1:l>=pt&&(s+=1),l>c&&(c=l,n={name:r.name??r.budget_id,spent:r.total_spend,allocated:o,pct:l})}const d=a>0?"alert":s>0?"warn":"ok",p=a>0?`${a} over limit`:s>0?`${s} near limit`:"All within budget";return{status:d,label:p,overCount:a,nearCount:s,cappedCount:i.length,worst:n}}const W=864e5,K=30;function T(){const e=new Date;return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`}function yt(){const[e,i]=b.useState(T);return b.useEffect(()=>{const a=()=>{if(document.visibilityState==="visible"){const s=T();i(n=>n===s?n:s)}};return document.addEventListener("visibilitychange",a),window.addEventListener("focus",a),()=>{document.removeEventListener("visibilitychange",a),window.removeEventListener("focus",a)}},[]),b.useMemo(()=>{const a=Date.now(),s=new Date(a);return{today:new Date(s.getFullYear(),s.getMonth(),s.getDate()).toISOString(),periodStart:new Date(a-K*W).toISOString(),prevStart:new Date(a-2*K*W).toISOString()}},[e])}const jt={ok:"Healthy",warn:"Elevated",alert:"High"},St={ok:"On track",warn:"Near limit",alert:"Over budget"};function Pt(){const e=at();return e.isLoading?t.jsx(st,{}):t.jsx(wt,{needsSetup:e.isSuccess&&e.data.providers.length===0})}function wt({needsSetup:e=!1}){var L,O,$,A,F,P,q;const i=yt(),a=b.useMemo(()=>({start_date:i.today}),[i]),s=b.useMemo(()=>({start_date:i.periodStart}),[i]),n=b.useMemo(()=>({start_date:i.prevStart,end_date:i.periodStart}),[i]),c=S(a,"hour"),d=S(s,"day"),p=S(n,"day"),r=nt(),o=ot(),l=it(),y=lt(),h=dt({},0,5),E=(L=c.data)==null?void 0:L.totals,v=(O=d.data)==null?void 0:O.totals,x=($=p.data)==null?void 0:$.totals,j=((A=d.data)==null?void 0:A.series)??[],C=j.length>1,u=M(v),k=M(x),Y=u.rate!==null&&k.rate!==null?w(u.rate,k.rate):null,m=bt(o.data??[]),V=gt(r.data),X=(l.data??[]).filter(f=>f.is_active).length,Z=(y.data??[]).filter(f=>!f.blocked).length,z=(((F=h.data)==null?void 0:F.length)??0)>0,J=e&&h.isSuccess&&!z,Q=c.error??d.error??r.error??o.error??l.error??y.error,tt=()=>{c.refetch(),d.refetch(),p.refetch(),r.refetch(),o.refetch(),l.refetch(),y.refetch(),h.refetch()},et=c.isFetching||d.isFetching||r.isFetching||o.isFetching||l.isFetching||y.isFetching||h.isFetching;return t.jsxs("div",{className:"flex flex-col gap-6",children:[t.jsx(ct,{title:"Overview",description:"At-a-glance spend, traffic, and health across the gateway.",action:t.jsx(ut,{onRefresh:tt,isFetching:et,updatedAt:d.dataUpdatedAt})}),J?t.jsx(Nt,{}):null,t.jsx(I,{error:Q}),t.jsx(_t,{providerHealth:V,healthy:((P=r.data)==null?void 0:P.healthy)??0,total:((q=r.data)==null?void 0:q.total)??0,budget:m,errStatus:u.status,errRate:u.rate,ready:r.isSuccess&&o.isSuccess&&d.isSuccess,failed:r.isError||o.isError||d.isError}),t.jsxs("div",{className:"grid grid-cols-2 gap-4 sm:grid-cols-3 xl:grid-cols-4",children:[t.jsx(g,{label:"Spend today",value:E?_(E.cost):"—"}),t.jsx(g,{label:"Spend, last 30 days",value:v?_(v.cost):"—",hint:v?t.jsx(N,{fraction:w(v.cost,x==null?void 0:x.cost)}):null,chart:C?t.jsx(U,{values:j.map(f=>f.cost),ariaLabel:"Spend trend over the last 30 days"}):void 0}),t.jsx(g,{label:"Requests, last 30 days",value:v?R(v.request_count):"—",hint:v?t.jsx(N,{fraction:w(v.request_count,x==null?void 0:x.request_count)}):null,chart:C?t.jsx(U,{values:j.map(f=>f.requests),ariaLabel:"Request volume trend over the last 30 days"}):void 0}),t.jsx(g,{label:"Error rate, last 30 days",value:u.rate===null?"—":D(u.rate),status:B(u.status),statusLabel:u.status==="neutral"?void 0:jt[u.status],hint:u.rate!==null?t.jsx(N,{fraction:Y}):null}),t.jsx(g,{label:"Budget health",value:o.data&&m.worst?D(m.worst.pct):"—",status:o.data?B(m.status):void 0,statusLabel:o.data&&m.status!=="neutral"?St[m.status]:void 0,hint:o.data?m.worst?`${m.label} · worst: ${m.worst.name}`:m.label:void 0,to:"/budgets"}),t.jsx(g,{label:"Active keys",value:l.data?R(X):"—",to:"/keys"}),t.jsx(g,{label:"Active users",value:y.data?R(Z):"—",to:"/users"})]}),t.jsx(Et,{entries:h.data??[],loading:h.isLoading,error:h.error})]})}function Nt(){const e=rt();return t.jsx(H,{children:t.jsxs(H.Content,{className:"flex flex-col gap-3 p-6",children:[t.jsxs("div",{children:[t.jsx("h2",{className:"text-lg font-semibold text-[var(--otari-ink)]",children:"Get started with Otari"}),t.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:"Add a provider to begin serving models. Once it is configured, this page will show your gateway’s traffic, spend, and health."})]}),t.jsx("div",{children:t.jsx(ht,{variant:"primary",onPress:()=>e("/providers"),children:"Add your first provider"})})]})})}function Rt({text:e}){return t.jsx("div",{role:"status",className:"flex items-center gap-2 rounded-xl border border-[var(--otari-line)] bg-[var(--otari-bg)] px-4 py-3 text-sm text-[var(--otari-muted)]",children:e})}function _t({providerHealth:e,healthy:i,total:a,budget:s,errStatus:n,errRate:c,ready:d,failed:p}){if(p)return t.jsx(Rt,{text:"Some status data could not be loaded."});if(!d)return null;const r=[];if((e==="warn"||e==="alert")&&a>0){const o=a-i;r.push({text:`${o} provider${o===1?"":"s"} unreachable`,to:"/providers"})}return s.overCount>0?r.push({text:`${s.overCount} budget${s.overCount===1?"":"s"} over limit`,to:"/budgets"}):s.nearCount>0&&r.push({text:`${s.nearCount} budget${s.nearCount===1?"":"s"} near limit`,to:"/budgets"}),n==="alert"&&c!==null&&r.push({text:`error rate ${D(c)}`,to:"/activity?status=error"}),r.length===0?null:t.jsxs("div",{role:"alert",className:"flex flex-col gap-2 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-900 sm:flex-row sm:flex-wrap sm:items-center",children:[t.jsx("span",{className:"font-medium",children:"Needs attention:"}),r.map((o,l)=>t.jsxs("span",{className:"flex items-center gap-2",children:[l>0?t.jsx("span",{"aria-hidden":!0,className:"text-amber-400",children:"·"}):null,t.jsx(G,{to:o.to,className:"underline underline-offset-2 hover:text-amber-950",children:o.text})]},o.to+o.text))]})}function Dt(e){return e==="error"?"error":"ok"}function Et({entries:e,loading:i,error:a}){const s=[{id:"time",header:"Time",cell:n=>t.jsx("span",{className:"text-[var(--otari-muted)]",title:new Date(n.timestamp).toLocaleString(),children:mt(n.timestamp)})},{id:"model",header:"Model",isRowHeader:!0,cell:n=>t.jsx("span",{className:"text-[var(--otari-ink)]",children:n.model})},{id:"cost",header:"Cost",align:"end",cell:n=>n.cost===null?"—":_(n.cost)},{id:"status",header:"Status",cell:n=>t.jsx("span",{className:`inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium ${n.status==="error"?"border-red-200 bg-red-50 text-red-700":"border-[var(--otari-line)] bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]"}`,children:Dt(n.status)})}];return t.jsxs("div",{className:"flex flex-col gap-3",children:[t.jsxs("div",{className:"flex items-center justify-between",children:[t.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Recent activity"}),t.jsx(G,{to:"/activity",className:"text-sm text-[var(--otari-brand-dark)] hover:underline",children:"View all →"})]}),t.jsx(I,{error:a}),t.jsx(vt,{ariaLabel:"Recent activity",columns:s,rows:e,getRowKey:n=>n.id,isLoading:i,emptyContent:"No requests yet. Once the gateway serves traffic, it appears here."})]})}export{Pt as OverviewIndex,wt as OverviewPage,T as localDayKey}; diff --git a/src/gateway/static/dashboard/assets/ProvidersPage-BP60GuZv.js b/src/gateway/static/dashboard/assets/ProvidersPage-BP60GuZv.js deleted file mode 100644 index 741f8b2f..00000000 --- a/src/gateway/static/dashboard/assets/ProvidersPage-BP60GuZv.js +++ /dev/null @@ -1 +0,0 @@ -import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as x,L as M}from"./react-dgEcD0HR.js";import{L as Y,X as G,J as V,N as W,Y as J,Z as X,_ as Z,P as Q,E as I,I as ee,$ as te,a0 as se,W as $,C as ae,a1 as O,a2 as ne,G as q,a3 as re,a4 as ie}from"./index-DV8qvdUj.js";import{F as S}from"./Field-Dwp6u3t8.js";import{D as oe}from"./DataTable-Bmn_eYSV.js";import{B as f,d as _,f as de,T as le,L as F,I as H,D as ce,S as me,C as T,a as ue,b as xe}from"./heroui-BI50yK5B.js";function R({value:s,onChange:a,label:t,placeholder:r,description:i}){return e.jsxs(le,{value:s,onChange:a,className:"flex max-w-md flex-col gap-1",children:[e.jsx(F,{className:"text-sm font-medium text-[var(--otari-ink)]",children:t}),e.jsx(H,{type:"password",placeholder:r??"sk-…",autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",spellCheck:!1,"data-1p-ignore":!0,"data-lpignore":"true"}),i?e.jsx(ce,{className:"text-xs text-[var(--otari-muted)]",children:i}):null]})}function z({label:s,value:a,onChange:t,description:r,placeholder:i,extra:l=[],includeCatalog:m=!0}){var k;const g=re(),u=x.useMemo(()=>m?[...l,...(g.data??[]).map(o=>({id:o.id,name:o.name}))]:l,[g.data,l,m]),[p,c]=x.useState(()=>{var o;return((o=u.find(y=>y.id===a))==null?void 0:o.name)??""}),j=((k=u.find(o=>o.id===a))==null?void 0:k.name)??"",h=p.trim()===j.trim()?"":p.trim().toLowerCase(),d=u.filter(o=>!h||o.name.toLowerCase().includes(h)||o.id.toLowerCase().includes(h)).slice(0,50);return e.jsxs(T.Root,{allowsEmptyCollection:!0,menuTrigger:"focus",inputValue:p,onInputChange:c,onSelectionChange:o=>{var y;o!=null?(t(String(o)),c(((y=u.find(N=>N.id===String(o)))==null?void 0:y.name)??"")):(t(""),c(""))},className:"flex max-w-md flex-col gap-1",children:[e.jsx(F,{className:"text-sm font-medium text-[var(--otari-ink)]",children:s}),e.jsxs(T.InputGroup,{children:[e.jsx(H,{placeholder:i??"Search providers…",autoComplete:"off","data-1p-ignore":!0,"data-lpignore":"true",onFocus:o=>o.currentTarget.select()}),e.jsx(T.Trigger,{})]}),e.jsx(T.Popover,{children:e.jsx(ue,{items:d,className:"max-h-72 overflow-auto",children:o=>e.jsx(xe,{id:o.id,textValue:o.name,children:o.name})})}),r?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:r}):null]})}function U({getPayload:s}){const a=ie(),t=s();return e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(f,{variant:"outline",isDisabled:t===null||a.isPending,onPress:()=>{t&&a.mutate(t)},children:a.isPending?"Testing…":"Test connection"}),e.jsx("span",{role:"status","aria-live":"polite",children:a.isPending?null:a.error?e.jsx("span",{className:"text-xs text-red-700",children:q(a.error)}):a.data?a.data.ok?e.jsxs("span",{className:"text-xs font-medium text-green-700",children:["Connected. ",a.data.model_count," model",a.data.model_count===1?"":"s"," available."]}):e.jsx("span",{className:"block max-w-md break-words text-xs text-red-700",children:a.data.error??"Connection failed."}):null})]})}function pe({onClose:s}){var w;const a=O(),[t,r]=x.useState(""),[i,l]=x.useState(""),[m,g]=x.useState(!1),[u,p]=x.useState(""),[c,j]=x.useState(""),h=ne(t),d=((w=h.data)==null?void 0:w.id)===t?h.data:void 0;x.useEffect(()=>{d&&p(d.default_api_base??"")},[d]);const k=(d==null?void 0:d.env_key_present)??!1,o=((d==null?void 0:d.requires_api_key)??!0)&&!k,y=c.trim()!==""&&c.trim()!==t,N=/[:/]/.test(c),P=t!==""&&!N&&(!o||i.trim()!=="")&&!a.isPending,C=()=>{P&&a.mutate({instance:y?c.trim():t,provider_type:y?t:null,api_base:u.trim()||null,api_key:i.trim()||null},{onSuccess:s})};return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx(I,{error:a.error}),e.jsx(z,{label:"Provider",value:t,onChange:A=>{r(A),j(""),p("")},description:"Its endpoint is built in."}),e.jsx(R,{value:i,onChange:l,label:d&&!o?"API key (optional)":"API key",description:d?o?`${d.name}'s endpoint is built in — just add your key.`:k?`${d.env_key} is set on the server, so a key is optional here. Paste one to override it.`:`${d.name} needs no API key.`:"Stored encrypted. Requires OTARI_SECRET_KEY on the server."}),e.jsx("button",{type:"button",className:"self-start text-xs font-medium text-[var(--otari-brand-dark)]",onClick:()=>g(A=>!A),children:m?"Hide advanced":"Advanced (API base, rename)"}),m?e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(S,{label:"API base",value:u,onChange:p,placeholder:(d==null?void 0:d.default_api_base)??"https://…/v1",description:"Only if you route through a proxy. Blank uses the built-in default."}),e.jsx(S,{label:"Name",value:c,onChange:j,placeholder:t||"instance name",description:N?e.jsx("span",{className:"text-red-700",children:"A name cannot contain “:” or “/”."}):"Rename to run two instances of the same provider."})]}):null,e.jsxs("div",{className:"flex flex-wrap items-start gap-2",children:[e.jsx(f,{variant:"primary",isDisabled:!P,onPress:C,children:a.isPending?"Adding…":"Add provider"}),e.jsx(f,{variant:"ghost",onPress:s,children:"Cancel"}),e.jsx(U,{getPayload:()=>t===""?null:{instance:y?c.trim():t,provider_type:y?t:null,api_base:u.trim()||null,api_key:i.trim()||null}})]})]})}function he({onClose:s}){const a=O(),[t,r]=x.useState(""),[i,l]=x.useState("openai-compatible"),[m,g]=x.useState(""),[u,p]=x.useState(""),c=/[:/]/.test(t),j=t.trim()!==""&&!c&&m.trim()!==""&&!a.isPending,h=()=>{j&&a.mutate({instance:t.trim(),provider_type:i||"openai-compatible",api_base:m.trim(),api_key:u.trim()||null},{onSuccess:s})};return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx(I,{error:a.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(S,{label:"Name",value:t,onChange:r,placeholder:"my-local-llm",isRequired:!0,autoFocus:!0,description:c?e.jsx("span",{className:"text-red-700",children:"A name cannot contain “:” or “/”."}):"Call it whatever you want."}),e.jsx(z,{label:"Compatible with",value:i,onChange:l,includeCatalog:!1,description:"The API this endpoint speaks.",extra:[{id:"openai-compatible",name:"OpenAI"},{id:"anthropic-compatible",name:"Anthropic"}]})]}),e.jsx(S,{label:"API base",value:m,onChange:g,placeholder:"http://localhost:8000/v1",isRequired:!0,description:"The endpoint URL of your server."}),e.jsx(R,{value:u,onChange:p,label:"API key (optional)",description:"Many local backends need none. Stored encrypted."}),e.jsxs("div",{className:"flex flex-wrap items-start gap-2",children:[e.jsx(f,{variant:"primary",isDisabled:!j,onPress:h,children:a.isPending?"Adding…":"Add provider"}),e.jsx(f,{variant:"ghost",onPress:s,children:"Cancel"}),e.jsx(U,{getPayload:()=>t.trim()===""||m.trim()===""?null:{instance:t.trim(),provider_type:i||"openai-compatible",api_base:m.trim(),api_key:u.trim()||null}})]})]})}function ve({onClose:s}){const[a,t]=x.useState("known");return e.jsx(_,{children:e.jsxs(_.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsx("div",{className:"flex items-center justify-between",children:e.jsx("div",{className:"flex items-center gap-1 rounded-lg bg-[var(--otari-bg)] p-1",children:[["known","Known provider"],["custom","Custom endpoint"]].map(([r,i])=>e.jsx("button",{type:"button","aria-pressed":a===r,onClick:()=>t(r),className:a===r?"rounded-md bg-white px-3 py-1.5 text-sm font-medium text-[var(--otari-ink)] shadow-sm":"rounded-md px-3 py-1.5 text-sm text-[var(--otari-muted)] hover:text-[var(--otari-ink)]",children:i},r))})}),a==="known"?e.jsx(pe,{onClose:s}):e.jsx(he,{onClose:s})]})})}function ge({provider:s,onClose:a}){const t=te(),[r,i]=x.useState(s.provider_type??""),[l,m]=x.useState(s.api_base??""),[g,u]=x.useState(!1),[p,c]=x.useState(""),j=()=>{if(t.isPending)return;const h={provider_type:r.trim()||null,api_base:l.trim()||null,expected_updated_at:s.updated_at};g&&p.trim()&&(h.api_key=p.trim()),t.mutate({instance:s.instance,body:h},{onSuccess:a})};return e.jsx(_,{children:e.jsxs(_.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsxs("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:["Edit ",e.jsx("code",{children:s.instance})]}),e.jsx(I,{error:t.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(S,{label:"Provider type",value:r,onChange:i,placeholder:"openai"}),e.jsx(S,{label:"API base",value:l,onChange:m,placeholder:"https://api.openai.com/v1"})]}),e.jsx("div",{className:"flex flex-col gap-2",children:g?e.jsxs(e.Fragment,{children:[e.jsx(R,{value:p,onChange:c,label:"New API key",description:"Stored encrypted. The old key is replaced when you save."}),e.jsx("button",{type:"button",className:"self-start text-xs font-medium text-[var(--otari-brand-dark)]",onClick:()=>{u(!1),c("")},children:"Keep the current key"})]}):e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"text-sm text-[var(--otari-muted)]",children:["API key: ",e.jsx("code",{children:s.last4?`••••${s.last4}`:"none set"})]}),e.jsx(f,{size:"sm",variant:"outline",onPress:()=>u(!0),children:"Replace key"})]})}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(f,{variant:"primary",isDisabled:t.isPending,onPress:j,children:t.isPending?"Saving…":"Save changes"}),e.jsx(f,{variant:"ghost",onPress:a,children:"Cancel"})]})]})})}function fe(s,a){const t=new Map((a??[]).map(l=>[l.instance,l])),r=new Map((s??[]).map(l=>[l.instance,l]));return[...new Set([...t.keys(),...r.keys()])].sort().map(l=>{const m=t.get(l);return{instance:l,source:m?"stored":"config",stored:m,meta:r.get(l)}})}function je({state:s}){return s?s.status==="pending"?e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:[e.jsx(me,{size:"sm"})," Testing…"]}):s.ok?e.jsxs("span",{className:"text-xs font-medium text-green-700",children:["Connected. ",s.model_count," model",s.model_count===1?"":"s"," available."]}):e.jsx("span",{className:"block max-w-xs break-words text-xs text-red-700",children:s.error??"Connection failed."}):null}function ye({health:s}){if(!s)return e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"—"});const a=s.ok?"border-green-200 bg-green-50 text-green-700":"border-red-200 bg-red-50 text-red-700",t=s.checked_at?`Last checked ${$(s.checked_at)}`:"Not checked yet",r=s.ok?t:`${s.error??"Unreachable"} · ${t}`;return e.jsxs("span",{title:r,className:`inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium ${a}`,children:[e.jsx("span",{"aria-hidden":!0,className:`h-1.5 w-1.5 rounded-full ${s.ok?"bg-green-500":"bg-red-500"}`}),s.ok?"Reachable":"Unreachable"]})}function be({healthy:s,total:a,checkedAt:t}){const r=s===a,i=se();return e.jsxs("div",{className:"flex flex-wrap items-center gap-3 rounded-xl border border-[var(--otari-line)] bg-[var(--otari-surface)] px-4 py-2.5 text-sm",children:[e.jsx("span",{"aria-hidden":!0,className:`h-2 w-2 rounded-full ${r?"bg-green-500":"bg-red-500"}`}),e.jsxs("span",{className:"font-medium text-[var(--otari-ink)]",children:[s," of ",a," provider",a===1?"":"s"," reachable"]}),t?e.jsxs("span",{className:"text-[var(--otari-muted)]",children:["Last checked ",$(t)]}):null,e.jsx(f,{size:"sm",variant:"ghost",className:"ml-auto",isDisabled:i.isPending,onPress:()=>i.mutate(),children:i.isPending?"Re-checking…":"Re-check all"})]})}function E({n:s,title:a,children:t}){return e.jsxs("li",{className:"flex gap-3",children:[e.jsx("span",{className:"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-[var(--otari-brand-tint)] text-xs font-semibold text-[var(--otari-brand-dark)]",children:s}),e.jsxs("div",{className:"text-sm",children:[e.jsx("div",{className:"font-medium text-[var(--otari-ink)]",children:a}),e.jsx("div",{className:"text-[var(--otari-muted)]",children:t})]})]})}function ke({onAddProvider:s,needsPricing:a,onEnablePricing:t,enabling:r,secretKeyConfigured:i}){return e.jsx(_,{children:e.jsxs(_.Content,{className:"flex flex-col gap-4 p-6",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold text-[var(--otari-ink)]",children:"Welcome to Otari"}),e.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:"You are signed in. Add a provider to start serving models: three quick steps."})]}),e.jsxs("ol",{className:"flex flex-col gap-3",children:[e.jsxs(E,{n:1,title:"Add a provider",children:["Enter a provider name (like ",e.jsx("code",{children:"openai"}),") and its API key. Keys are encrypted at rest."]}),e.jsxs(E,{n:2,title:"Test the connection",children:["Use ",e.jsx("strong",{children:"Test"})," on the provider row to confirm the key works and see how many models it serves."]}),e.jsxs(E,{n:3,title:"Send your first request",children:["Point your app at ",e.jsx("code",{children:"/v1"})," on this gateway with the API key printed in the server logs (",e.jsx("code",{children:"gw-…"}),"). See the"," ",e.jsx("a",{href:"/welcome",className:"font-medium text-[var(--otari-brand-dark)]",children:"quickstart"}),"."]})]}),a?e.jsxs("p",{className:"text-sm text-[var(--otari-muted)]",children:["Tip: ",e.jsx("code",{children:"require_pricing"})," is on, so requests are rejected until pricing is set."," ",e.jsx("button",{type:"button",className:"font-medium text-[var(--otari-brand-dark)] disabled:opacity-50",disabled:r,onClick:t,children:"Enable default pricing"})," ","to meter new models with public rates."]}):null,e.jsx("div",{children:e.jsx(f,{variant:"primary",isDisabled:!i,onPress:s,children:"Add your first provider"})})]})})}function we(){var K,B,D,L;const s=Y(),a=G(),t=V(),r=W(),i=J(),l=X(),m=Z(),[g,u]=x.useState(!1),[p,c]=x.useState(null),[j,h]=x.useState({}),d=fe((K=s.data)==null?void 0:K.providers,a.data),k=new Map((((B=r.data)==null?void 0:B.providers)??[]).map(n=>[n.instance,n])),o=s.isLoading||a.isLoading,y=((D=a.data)==null?void 0:D.find(n=>n.instance===p))??null,N=((L=t.data)==null?void 0:L.require_pricing)===!0&&t.data.default_pricing===!1,P=t.data?t.data.secret_key_configured!==!1:!t.isError,C=!o&&d.length===0&&!g,w=n=>{h(v=>({...v,[n]:{status:"pending"}})),l.mutate(n,{onSuccess:v=>h(b=>({...b,[n]:{status:"done",...v}})),onError:v=>h(b=>({...b,[n]:{status:"done",ok:!1,model_count:0,error:q(v)}}))})},A=[{id:"provider",header:"Provider",isRowHeader:!0,cell:n=>e.jsx(M,{to:`/models?provider=${encodeURIComponent(n.instance)}`,className:"font-medium text-[var(--otari-ink)] hover:text-[var(--otari-brand-dark)] hover:underline",children:n.instance})},{id:"type",header:"Type",cell:n=>{var v,b;return e.jsx("span",{className:"text-[var(--otari-muted)]",children:((v=n.meta)==null?void 0:v.provider_type)??((b=n.stored)==null?void 0:b.provider_type)??n.instance})}},{id:"source",header:"Source",cell:n=>e.jsx(de,{size:"sm",color:n.source==="stored"?"accent":"default",children:n.source==="stored"?"stored":"config"})},{id:"api_key",header:"API key",cell:n=>{var v,b;return e.jsx("span",{className:"text-[var(--otari-muted)]",children:n.source==="stored"?n.stored&&!n.stored.decryptable?e.jsx("span",{className:"text-amber-700",title:"This key can't be decrypted with the current OTARI_SECRET_KEY. Replace the key, or restore the original OTARI_SECRET_KEY.",children:"⚠ key unreadable"}):e.jsx("code",{children:(v=n.stored)!=null&&v.last4?`••••${n.stored.last4}`:"none set"}):(b=n.meta)!=null&&b.env_key?e.jsxs("span",{children:["via ",e.jsx("code",{children:n.meta.env_key})]}):"config.yml"})}},{id:"status",header:"Status",cell:n=>e.jsx(ye,{health:k.get(n.instance)})},{id:"actions",header:"Actions",align:"end",cell:n=>{var v,b;return n.source==="stored"?e.jsxs("div",{className:"flex flex-col items-end gap-1.5",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx(f,{size:"sm",variant:"outline",isDisabled:((v=j[n.instance])==null?void 0:v.status)==="pending"||((b=n.stored)==null?void 0:b.decryptable)===!1,onPress:()=>w(n.instance),children:"Test"}),e.jsx(f,{size:"sm",variant:"ghost",onPress:()=>{u(!1),c(n.instance)},children:"Edit"}),e.jsx(ae,{confirmLabel:"Delete",isPending:i.isPending,onConfirm:()=>i.mutate(n.instance),children:"Delete"})]}),e.jsx(je,{state:j[n.instance]})]}):e.jsx("span",{className:"block text-right text-xs text-[var(--otari-muted)]",children:"managed in config.yml"})}}];return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(Q,{title:"Providers",description:"Add provider API keys here to serve models without editing config.yml. Keys are encrypted at rest.",action:g||C?null:e.jsx(f,{variant:"primary",isDisabled:!P,onPress:()=>{c(null),u(!0)},children:"Add provider"})}),e.jsx(I,{error:s.error??a.error??t.error??r.error??m.error??i.error}),P?null:e.jsxs(ee,{tone:"warning",children:[e.jsx("code",{children:"OTARI_SECRET_KEY"})," is not set, so provider keys can't be encrypted at rest and adding providers from the dashboard is disabled. Set it on the server and restart to add providers here. Providers defined in"," ",e.jsx("code",{children:"config.yml"})," keep working without it."]}),C?e.jsx(ke,{onAddProvider:()=>{c(null),u(!0)},needsPricing:N,onEnablePricing:()=>m.mutate({default_pricing:!0}),enabling:m.isPending,secretKeyConfigured:P}):null,g&&P?e.jsx(ve,{onClose:()=>u(!1)}):null,y?e.jsx(ge,{provider:y,onClose:()=>c(null)}):null,!o&&d.length>0&&r.data&&r.data.total>0?e.jsx(be,{healthy:r.data.healthy,total:r.data.total,checkedAt:r.data.checked_at}):null,C?null:e.jsx(oe,{ariaLabel:"Providers",columns:A,rows:d,getRowKey:n=>n.instance,isLoading:o,emptyContent:"No providers yet. Add your first provider to start serving models."})]})}export{we as ProvidersPage}; diff --git a/src/gateway/static/dashboard/assets/ProvidersPage-CC2Glu9P.js b/src/gateway/static/dashboard/assets/ProvidersPage-CC2Glu9P.js new file mode 100644 index 00000000..bc1c0e8b --- /dev/null +++ b/src/gateway/static/dashboard/assets/ProvidersPage-CC2Glu9P.js @@ -0,0 +1 @@ +import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as x,L as M}from"./react-dgEcD0HR.js";import{T as Y,a1 as V,Q as G,V as Q,a2 as W,a3 as J,a4 as X,P as Z,E as I,I as ee,a5 as te,a6 as se,a0 as O,o as ae,a7 as $,a8 as ne,N as q,a9 as re,aa as ie}from"./index-Br3lxdY5.js";import{F as S}from"./Field-Dwp6u3t8.js";import{D as oe}from"./DataTable-Bmn_eYSV.js";import{B as f,d as _,f as de,T as le,L as F,I as H,D as ce,S as me,C as T,a as ue,b as xe}from"./heroui-BI50yK5B.js";function R({value:s,onChange:a,label:t,placeholder:r,description:i}){return e.jsxs(le,{value:s,onChange:a,className:"flex max-w-md flex-col gap-1",children:[e.jsx(F,{className:"text-sm font-medium text-[var(--otari-ink)]",children:t}),e.jsx(H,{type:"password",placeholder:r??"sk-…",autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",spellCheck:!1,"data-1p-ignore":!0,"data-lpignore":"true"}),i?e.jsx(ce,{className:"text-xs text-[var(--otari-muted)]",children:i}):null]})}function z({label:s,value:a,onChange:t,description:r,placeholder:i,extra:l=[],includeCatalog:m=!0}){var k;const g=re(),u=x.useMemo(()=>m?[...l,...(g.data??[]).map(o=>({id:o.id,name:o.name}))]:l,[g.data,l,m]),[p,c]=x.useState(()=>{var o;return((o=u.find(y=>y.id===a))==null?void 0:o.name)??""}),j=((k=u.find(o=>o.id===a))==null?void 0:k.name)??"",h=p.trim()===j.trim()?"":p.trim().toLowerCase(),d=u.filter(o=>!h||o.name.toLowerCase().includes(h)||o.id.toLowerCase().includes(h)).slice(0,50);return e.jsxs(T.Root,{allowsEmptyCollection:!0,menuTrigger:"focus",inputValue:p,onInputChange:c,onSelectionChange:o=>{var y;o!=null?(t(String(o)),c(((y=u.find(N=>N.id===String(o)))==null?void 0:y.name)??"")):(t(""),c(""))},className:"flex max-w-md flex-col gap-1",children:[e.jsx(F,{className:"text-sm font-medium text-[var(--otari-ink)]",children:s}),e.jsxs(T.InputGroup,{children:[e.jsx(H,{placeholder:i??"Search providers…",autoComplete:"off","data-1p-ignore":!0,"data-lpignore":"true",onFocus:o=>o.currentTarget.select()}),e.jsx(T.Trigger,{})]}),e.jsx(T.Popover,{children:e.jsx(ue,{items:d,className:"max-h-72 overflow-auto",children:o=>e.jsx(xe,{id:o.id,textValue:o.name,children:o.name})})}),r?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:r}):null]})}function U({getPayload:s}){const a=ie(),t=s();return e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(f,{variant:"outline",isDisabled:t===null||a.isPending,onPress:()=>{t&&a.mutate(t)},children:a.isPending?"Testing…":"Test connection"}),e.jsx("span",{role:"status","aria-live":"polite",children:a.isPending?null:a.error?e.jsx("span",{className:"text-xs text-red-700",children:q(a.error)}):a.data?a.data.ok?e.jsxs("span",{className:"text-xs font-medium text-green-700",children:["Connected. ",a.data.model_count," model",a.data.model_count===1?"":"s"," available."]}):e.jsx("span",{className:"block max-w-md break-words text-xs text-red-700",children:a.data.error??"Connection failed."}):null})]})}function pe({onClose:s}){var w;const a=$(),[t,r]=x.useState(""),[i,l]=x.useState(""),[m,g]=x.useState(!1),[u,p]=x.useState(""),[c,j]=x.useState(""),h=ne(t),d=((w=h.data)==null?void 0:w.id)===t?h.data:void 0;x.useEffect(()=>{d&&p(d.default_api_base??"")},[d]);const k=(d==null?void 0:d.env_key_present)??!1,o=((d==null?void 0:d.requires_api_key)??!0)&&!k,y=c.trim()!==""&&c.trim()!==t,N=/[:/]/.test(c),P=t!==""&&!N&&(!o||i.trim()!=="")&&!a.isPending,C=()=>{P&&a.mutate({instance:y?c.trim():t,provider_type:y?t:null,api_base:u.trim()||null,api_key:i.trim()||null},{onSuccess:s})};return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx(I,{error:a.error}),e.jsx(z,{label:"Provider",value:t,onChange:A=>{r(A),j(""),p("")},description:"Its endpoint is built in."}),e.jsx(R,{value:i,onChange:l,label:d&&!o?"API key (optional)":"API key",description:d?o?`${d.name}'s endpoint is built in — just add your key.`:k?`${d.env_key} is set on the server, so a key is optional here. Paste one to override it.`:`${d.name} needs no API key.`:"Stored encrypted. Requires OTARI_SECRET_KEY on the server."}),e.jsx("button",{type:"button",className:"self-start text-xs font-medium text-[var(--otari-brand-dark)]",onClick:()=>g(A=>!A),children:m?"Hide advanced":"Advanced (API base, rename)"}),m?e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(S,{label:"API base",value:u,onChange:p,placeholder:(d==null?void 0:d.default_api_base)??"https://…/v1",description:"Only if you route through a proxy. Blank uses the built-in default."}),e.jsx(S,{label:"Name",value:c,onChange:j,placeholder:t||"instance name",description:N?e.jsx("span",{className:"text-red-700",children:"A name cannot contain “:” or “/”."}):"Rename to run two instances of the same provider."})]}):null,e.jsxs("div",{className:"flex flex-wrap items-start gap-2",children:[e.jsx(f,{variant:"primary",isDisabled:!P,onPress:C,children:a.isPending?"Adding…":"Add provider"}),e.jsx(f,{variant:"ghost",onPress:s,children:"Cancel"}),e.jsx(U,{getPayload:()=>t===""?null:{instance:y?c.trim():t,provider_type:y?t:null,api_base:u.trim()||null,api_key:i.trim()||null}})]})]})}function he({onClose:s}){const a=$(),[t,r]=x.useState(""),[i,l]=x.useState("openai-compatible"),[m,g]=x.useState(""),[u,p]=x.useState(""),c=/[:/]/.test(t),j=t.trim()!==""&&!c&&m.trim()!==""&&!a.isPending,h=()=>{j&&a.mutate({instance:t.trim(),provider_type:i||"openai-compatible",api_base:m.trim(),api_key:u.trim()||null},{onSuccess:s})};return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx(I,{error:a.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(S,{label:"Name",value:t,onChange:r,placeholder:"my-local-llm",isRequired:!0,autoFocus:!0,description:c?e.jsx("span",{className:"text-red-700",children:"A name cannot contain “:” or “/”."}):"Call it whatever you want."}),e.jsx(z,{label:"Compatible with",value:i,onChange:l,includeCatalog:!1,description:"The API this endpoint speaks.",extra:[{id:"openai-compatible",name:"OpenAI"},{id:"anthropic-compatible",name:"Anthropic"}]})]}),e.jsx(S,{label:"API base",value:m,onChange:g,placeholder:"http://localhost:8000/v1",isRequired:!0,description:"The endpoint URL of your server."}),e.jsx(R,{value:u,onChange:p,label:"API key (optional)",description:"Many local backends need none. Stored encrypted."}),e.jsxs("div",{className:"flex flex-wrap items-start gap-2",children:[e.jsx(f,{variant:"primary",isDisabled:!j,onPress:h,children:a.isPending?"Adding…":"Add provider"}),e.jsx(f,{variant:"ghost",onPress:s,children:"Cancel"}),e.jsx(U,{getPayload:()=>t.trim()===""||m.trim()===""?null:{instance:t.trim(),provider_type:i||"openai-compatible",api_base:m.trim(),api_key:u.trim()||null}})]})]})}function ve({onClose:s}){const[a,t]=x.useState("known");return e.jsx(_,{children:e.jsxs(_.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsx("div",{className:"flex items-center justify-between",children:e.jsx("div",{className:"flex items-center gap-1 rounded-lg bg-[var(--otari-bg)] p-1",children:[["known","Known provider"],["custom","Custom endpoint"]].map(([r,i])=>e.jsx("button",{type:"button","aria-pressed":a===r,onClick:()=>t(r),className:a===r?"rounded-md bg-white px-3 py-1.5 text-sm font-medium text-[var(--otari-ink)] shadow-sm":"rounded-md px-3 py-1.5 text-sm text-[var(--otari-muted)] hover:text-[var(--otari-ink)]",children:i},r))})}),a==="known"?e.jsx(pe,{onClose:s}):e.jsx(he,{onClose:s})]})})}function ge({provider:s,onClose:a}){const t=te(),[r,i]=x.useState(s.provider_type??""),[l,m]=x.useState(s.api_base??""),[g,u]=x.useState(!1),[p,c]=x.useState(""),j=()=>{if(t.isPending)return;const h={provider_type:r.trim()||null,api_base:l.trim()||null,expected_updated_at:s.updated_at};g&&p.trim()&&(h.api_key=p.trim()),t.mutate({instance:s.instance,body:h},{onSuccess:a})};return e.jsx(_,{children:e.jsxs(_.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsxs("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:["Edit ",e.jsx("code",{children:s.instance})]}),e.jsx(I,{error:t.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(S,{label:"Provider type",value:r,onChange:i,placeholder:"openai"}),e.jsx(S,{label:"API base",value:l,onChange:m,placeholder:"https://api.openai.com/v1"})]}),e.jsx("div",{className:"flex flex-col gap-2",children:g?e.jsxs(e.Fragment,{children:[e.jsx(R,{value:p,onChange:c,label:"New API key",description:"Stored encrypted. The old key is replaced when you save."}),e.jsx("button",{type:"button",className:"self-start text-xs font-medium text-[var(--otari-brand-dark)]",onClick:()=>{u(!1),c("")},children:"Keep the current key"})]}):e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"text-sm text-[var(--otari-muted)]",children:["API key: ",e.jsx("code",{children:s.last4?`••••${s.last4}`:"none set"})]}),e.jsx(f,{size:"sm",variant:"outline",onPress:()=>u(!0),children:"Replace key"})]})}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(f,{variant:"primary",isDisabled:t.isPending,onPress:j,children:t.isPending?"Saving…":"Save changes"}),e.jsx(f,{variant:"ghost",onPress:a,children:"Cancel"})]})]})})}function fe(s,a){const t=new Map((a??[]).map(l=>[l.instance,l])),r=new Map((s??[]).map(l=>[l.instance,l]));return[...new Set([...t.keys(),...r.keys()])].sort().map(l=>{const m=t.get(l);return{instance:l,source:m?"stored":"config",stored:m,meta:r.get(l)}})}function je({state:s}){return s?s.status==="pending"?e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-xs text-[var(--otari-muted)]",children:[e.jsx(me,{size:"sm"})," Testing…"]}):s.ok?e.jsxs("span",{className:"text-xs font-medium text-green-700",children:["Connected. ",s.model_count," model",s.model_count===1?"":"s"," available."]}):e.jsx("span",{className:"block max-w-xs break-words text-xs text-red-700",children:s.error??"Connection failed."}):null}function ye({health:s}){if(!s)return e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"—"});const a=s.ok?"border-green-200 bg-green-50 text-green-700":"border-red-200 bg-red-50 text-red-700",t=s.checked_at?`Last checked ${O(s.checked_at)}`:"Not checked yet",r=s.ok?t:`${s.error??"Unreachable"} · ${t}`;return e.jsxs("span",{title:r,className:`inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium ${a}`,children:[e.jsx("span",{"aria-hidden":!0,className:`h-1.5 w-1.5 rounded-full ${s.ok?"bg-green-500":"bg-red-500"}`}),s.ok?"Reachable":"Unreachable"]})}function be({healthy:s,total:a,checkedAt:t}){const r=s===a,i=se();return e.jsxs("div",{className:"flex flex-wrap items-center gap-3 rounded-xl border border-[var(--otari-line)] bg-[var(--otari-surface)] px-4 py-2.5 text-sm",children:[e.jsx("span",{"aria-hidden":!0,className:`h-2 w-2 rounded-full ${r?"bg-green-500":"bg-red-500"}`}),e.jsxs("span",{className:"font-medium text-[var(--otari-ink)]",children:[s," of ",a," provider",a===1?"":"s"," reachable"]}),t?e.jsxs("span",{className:"text-[var(--otari-muted)]",children:["Last checked ",O(t)]}):null,e.jsx(f,{size:"sm",variant:"ghost",className:"ml-auto",isDisabled:i.isPending,onPress:()=>i.mutate(),children:i.isPending?"Re-checking…":"Re-check all"})]})}function E({n:s,title:a,children:t}){return e.jsxs("li",{className:"flex gap-3",children:[e.jsx("span",{className:"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-[var(--otari-brand-tint)] text-xs font-semibold text-[var(--otari-brand-dark)]",children:s}),e.jsxs("div",{className:"text-sm",children:[e.jsx("div",{className:"font-medium text-[var(--otari-ink)]",children:a}),e.jsx("div",{className:"text-[var(--otari-muted)]",children:t})]})]})}function ke({onAddProvider:s,needsPricing:a,onEnablePricing:t,enabling:r,secretKeyConfigured:i}){return e.jsx(_,{children:e.jsxs(_.Content,{className:"flex flex-col gap-4 p-6",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold text-[var(--otari-ink)]",children:"Welcome to Otari"}),e.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:"You are signed in. Add a provider to start serving models: three quick steps."})]}),e.jsxs("ol",{className:"flex flex-col gap-3",children:[e.jsxs(E,{n:1,title:"Add a provider",children:["Enter a provider name (like ",e.jsx("code",{children:"openai"}),") and its API key. Keys are encrypted at rest."]}),e.jsxs(E,{n:2,title:"Test the connection",children:["Use ",e.jsx("strong",{children:"Test"})," on the provider row to confirm the key works and see how many models it serves."]}),e.jsxs(E,{n:3,title:"Send your first request",children:["Point your app at ",e.jsx("code",{children:"/v1"})," on this gateway with the API key printed in the server logs (",e.jsx("code",{children:"gw-…"}),"). See the"," ",e.jsx("a",{href:"/welcome",className:"font-medium text-[var(--otari-brand-dark)]",children:"quickstart"}),"."]})]}),a?e.jsxs("p",{className:"text-sm text-[var(--otari-muted)]",children:["Tip: ",e.jsx("code",{children:"require_pricing"})," is on, so requests are rejected until pricing is set."," ",e.jsx("button",{type:"button",className:"font-medium text-[var(--otari-brand-dark)] disabled:opacity-50",disabled:r,onClick:t,children:"Enable default pricing"})," ","to meter new models with public rates."]}):null,e.jsx("div",{children:e.jsx(f,{variant:"primary",isDisabled:!i,onPress:s,children:"Add your first provider"})})]})})}function we(){var K,B,D,L;const s=Y(),a=V(),t=G(),r=Q(),i=W(),l=J(),m=X(),[g,u]=x.useState(!1),[p,c]=x.useState(null),[j,h]=x.useState({}),d=fe((K=s.data)==null?void 0:K.providers,a.data),k=new Map((((B=r.data)==null?void 0:B.providers)??[]).map(n=>[n.instance,n])),o=s.isLoading||a.isLoading,y=((D=a.data)==null?void 0:D.find(n=>n.instance===p))??null,N=((L=t.data)==null?void 0:L.require_pricing)===!0&&t.data.default_pricing===!1,P=t.data?t.data.secret_key_configured!==!1:!t.isError,C=!o&&d.length===0&&!g,w=n=>{h(v=>({...v,[n]:{status:"pending"}})),l.mutate(n,{onSuccess:v=>h(b=>({...b,[n]:{status:"done",...v}})),onError:v=>h(b=>({...b,[n]:{status:"done",ok:!1,model_count:0,error:q(v)}}))})},A=[{id:"provider",header:"Provider",isRowHeader:!0,cell:n=>e.jsx(M,{to:`/models?provider=${encodeURIComponent(n.instance)}`,className:"font-medium text-[var(--otari-ink)] hover:text-[var(--otari-brand-dark)] hover:underline",children:n.instance})},{id:"type",header:"Type",cell:n=>{var v,b;return e.jsx("span",{className:"text-[var(--otari-muted)]",children:((v=n.meta)==null?void 0:v.provider_type)??((b=n.stored)==null?void 0:b.provider_type)??n.instance})}},{id:"source",header:"Source",cell:n=>e.jsx(de,{size:"sm",color:n.source==="stored"?"accent":"default",children:n.source==="stored"?"stored":"config"})},{id:"api_key",header:"API key",cell:n=>{var v,b;return e.jsx("span",{className:"text-[var(--otari-muted)]",children:n.source==="stored"?n.stored&&!n.stored.decryptable?e.jsx("span",{className:"text-amber-700",title:"This key can't be decrypted with the current OTARI_SECRET_KEY. Replace the key, or restore the original OTARI_SECRET_KEY.",children:"⚠ key unreadable"}):e.jsx("code",{children:(v=n.stored)!=null&&v.last4?`••••${n.stored.last4}`:"none set"}):(b=n.meta)!=null&&b.env_key?e.jsxs("span",{children:["via ",e.jsx("code",{children:n.meta.env_key})]}):"config.yml"})}},{id:"status",header:"Status",cell:n=>e.jsx(ye,{health:k.get(n.instance)})},{id:"actions",header:"Actions",align:"end",cell:n=>{var v,b;return n.source==="stored"?e.jsxs("div",{className:"flex flex-col items-end gap-1.5",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx(f,{size:"sm",variant:"outline",isDisabled:((v=j[n.instance])==null?void 0:v.status)==="pending"||((b=n.stored)==null?void 0:b.decryptable)===!1,onPress:()=>w(n.instance),children:"Test"}),e.jsx(f,{size:"sm",variant:"ghost",onPress:()=>{u(!1),c(n.instance)},children:"Edit"}),e.jsx(ae,{confirmLabel:"Delete",isPending:i.isPending,onConfirm:()=>i.mutate(n.instance),children:"Delete"})]}),e.jsx(je,{state:j[n.instance]})]}):e.jsx("span",{className:"block text-right text-xs text-[var(--otari-muted)]",children:"managed in config.yml"})}}];return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(Z,{title:"Providers",description:"Add provider API keys here to serve models without editing config.yml. Keys are encrypted at rest.",action:g||C?null:e.jsx(f,{variant:"primary",isDisabled:!P,onPress:()=>{c(null),u(!0)},children:"Add provider"})}),e.jsx(I,{error:s.error??a.error??t.error??r.error??m.error??i.error}),P?null:e.jsxs(ee,{tone:"warning",children:[e.jsx("code",{children:"OTARI_SECRET_KEY"})," is not set, so provider keys can't be encrypted at rest and adding providers from the dashboard is disabled. Set it on the server and restart to add providers here. Providers defined in"," ",e.jsx("code",{children:"config.yml"})," keep working without it."]}),C?e.jsx(ke,{onAddProvider:()=>{c(null),u(!0)},needsPricing:N,onEnablePricing:()=>m.mutate({default_pricing:!0}),enabling:m.isPending,secretKeyConfigured:P}):null,g&&P?e.jsx(ve,{onClose:()=>u(!1)}):null,y?e.jsx(ge,{provider:y,onClose:()=>c(null)}):null,!o&&d.length>0&&r.data&&r.data.total>0?e.jsx(be,{healthy:r.data.healthy,total:r.data.total,checkedAt:r.data.checked_at}):null,C?null:e.jsx(oe,{ariaLabel:"Providers",columns:A,rows:d,getRowKey:n=>n.instance,isLoading:o,emptyContent:"No providers yet. Add your first provider to start serving models."})]})}export{we as ProvidersPage}; diff --git a/src/gateway/static/dashboard/assets/SettingsPage-CYDMBdEO.js b/src/gateway/static/dashboard/assets/SettingsPage-CYDMBdEO.js new file mode 100644 index 00000000..cbea6d10 --- /dev/null +++ b/src/gateway/static/dashboard/assets/SettingsPage-CYDMBdEO.js @@ -0,0 +1 @@ +import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as u}from"./react-dgEcD0HR.js";import{Q as R,a4 as C,P,E as y,U as E,ab as T,ac as _,ad as D,F as A,ae as O,a1 as F,af as I,I as w}from"./index-Br3lxdY5.js";import{d as v,B as h,A as d,g as K,I as z}from"./heroui-BI50yK5B.js";function b(t,r){return{[t]:r}}function M(t,r){let s=0;for(const a of r)if(a===t[s]&&(s+=1),s===t.length)return!0;return t.length===0}function L(t,r){const s=r.trim().toLowerCase();if(s==="")return!0;const a=`${t.key} ${t.description??""} ${t.group}`.toLowerCase(),n=t.key.toLowerCase().replace(/[^a-z0-9]/g,"");return s.split(/\s+/).every(i=>a.includes(i)||M(i,n))}function B({checked:t,onChange:r,label:s,disabled:a}){return e.jsx("button",{type:"button",role:"switch","aria-checked":t,"aria-label":s,disabled:a,onClick:()=>r(!t),className:`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors disabled:opacity-50 ${t?"bg-[var(--otari-brand)]":"bg-[var(--otari-line)]"}`,children:e.jsx("span",{className:`inline-block h-5 w-5 transform rounded-full bg-white shadow transition-transform ${t?"translate-x-5":"translate-x-0.5"}`})})}function $({field:t,onSave:r,disabled:s}){const a=typeof t.value=="number"?t.value:0,[n,i]=u.useState(String(a)),o=t.type==="float";u.useEffect(()=>{i(String(a))},[a]);const c=Number(n),p=n.trim()!==""&&Number.isFinite(c)&&(o||Number.isInteger(c)),m=t.minimum??void 0,x=t.exclusive_minimum??void 0,g=x!==void 0?c>x:m!==void 0?c>=m:c>=0,l=p&&g&&c!==a;return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(z,{type:"number",min:"0",step:o?"any":"1",inputMode:o?"decimal":"numeric","aria-label":t.key,value:n,disabled:s,onChange:f=>i(f.target.value),className:"w-28 rounded-md border border-[var(--otari-line)] bg-white px-2 py-1 text-right text-sm tabular-nums focus:border-[var(--otari-brand)] focus:outline-none disabled:opacity-50"}),e.jsx(h,{size:"sm",variant:"primary","aria-label":`Save ${t.key}`,isDisabled:s||!l,onPress:()=>r(c),children:"Save"})]})}function H({field:t,onSave:r,disabled:s}){const a=typeof t.value=="string"?t.value:"",[n,i]=u.useState(a);u.useEffect(()=>{i(a)},[a]);const o=n!==a;return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"text","aria-label":t.key,value:n,disabled:s,placeholder:"unset",onChange:c=>i(c.target.value),className:"w-56 rounded-md border border-[var(--otari-line)] bg-white px-2 py-1 text-sm focus:border-[var(--otari-brand)] focus:outline-none disabled:opacity-50"}),e.jsx(h,{size:"sm",variant:"primary","aria-label":`Save ${t.key}`,isDisabled:s||!o,onPress:()=>r(n.trim()===""?null:n),children:"Save"})]})}function Y(t){const{value:r}=t;return r==null?"unset":typeof r=="boolean"?r?"on":"off":Array.isArray(r)?r.length?r.join(", "):"none":String(r)}function q({field:t,patch:r,disabled:s}){return t.settable?t.type==="bool"?e.jsx(B,{checked:t.value===!0,onChange:a=>r(b(t.key,a)),label:t.key,disabled:s}):t.options&&t.options.length>0?e.jsx(A,{ariaLabel:t.key,value:String(t.value??""),onChange:a=>r(b(t.key,a)),options:t.options.map(a=>({value:a,label:a}))}):t.type==="int"||t.type==="float"?e.jsx($,{field:t,onSave:a=>r(b(t.key,a)),disabled:s}):e.jsx(H,{field:t,onSave:a=>r(b(t.key,a)),disabled:s}):e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-sm tabular-nums text-[var(--otari-ink)]",children:Y(t)}),e.jsx("span",{className:"rounded-full border border-[var(--otari-line)] px-2 py-0.5 text-xs text-[var(--otari-muted)]",children:"startup-only"})]})}function U({field:t,patch:r,disabled:s}){return e.jsxs("div",{className:"flex items-start justify-between gap-6 py-4",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("code",{className:"text-sm font-medium text-[var(--otari-ink)]",children:t.key}),t.description?e.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:t.description}):null]}),e.jsx("div",{className:"shrink-0 pt-0.5",children:e.jsx(q,{field:t,patch:r,disabled:s})})]})}function V({value:t,fieldRef:r}){const s=u.useRef(null),a=r??s,[n,i]=u.useState(!1),[o,c]=u.useState(!1),p=async()=>{var m,x,g;(m=a.current)==null||m.focus(),(x=a.current)==null||x.select();try{if((g=navigator.clipboard)!=null&&g.writeText){await navigator.clipboard.writeText(t),i(!0),c(!1),window.setTimeout(()=>i(!1),2e3);return}}catch{}c(!0)};return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-xs font-medium text-[var(--otari-muted)]",children:"New master key"}),e.jsx(h,{size:"sm",variant:"outline",onPress:p,children:n?"Copied":"Copy"})]}),e.jsx("input",{ref:a,readOnly:!0,value:t,onFocus:m=>m.currentTarget.select(),autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",spellCheck:!1,"data-1p-ignore":!0,"data-lpignore":"true"}),e.jsx("span",{"aria-live":"polite",className:"text-xs text-[var(--otari-brand-dark)]",children:n?"Copied to clipboard.":""}),o?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Selected. Press Ctrl/Cmd-C to copy."}):null]})}function G({masterKey:t,error:r,isPending:s,onRegenerate:a,onClose:n}){const i=u.useRef(null);return u.useEffect(()=>{var o,c;t!==void 0&&((o=i.current)==null||o.focus(),(c=i.current)==null||c.select())},[t]),e.jsx(d.Backdrop,{children:e.jsx(d.Container,{placement:"center",size:"lg",children:e.jsxs(d.Dialog,{children:[e.jsx(d.Header,{children:e.jsx(d.Heading,{children:t!==void 0?"Master key regenerated":"Regenerate master key?"})}),e.jsx(d.Body,{className:"flex flex-col gap-4",children:t!==void 0?e.jsxs(e.Fragment,{children:[e.jsx(w,{tone:"warning",children:"Copy this key now. It is shown once and cannot be retrieved again after you close this dialog."}),e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:"The previous master key has stopped working. This browser tab now uses the new key."}),e.jsx(V,{value:t,fieldRef:i})]}):e.jsxs(e.Fragment,{children:[e.jsx(w,{tone:"warning",children:"This immediately invalidates the current dashboard master key. Other signed-in dashboard sessions will need the new key to continue."}),e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:"The replacement key will be shown once. Save it before closing the next screen."}),e.jsx(y,{error:r})]})}),e.jsx(d.Footer,{children:t!==void 0?e.jsx(h,{variant:"primary",onPress:n,children:"I’ve saved this key"}):e.jsxs(e.Fragment,{children:[e.jsx(h,{variant:"ghost",isDisabled:s,onPress:n,children:"Cancel"}),e.jsx(h,{variant:"danger",isPending:s,onPress:a,children:"Regenerate key"})]})})]})})})}function Q({source:t}){const r=O(),[s,a]=u.useState(!1),[n,i]=u.useState(),o=t==="generated",c=()=>r.mutate(void 0,{onSuccess:x=>{i(x.master_key)}}),p=()=>{a(!1),i(void 0),r.reset()},m=x=>{x?(r.reset(),a(!0)):n===void 0&&p()};return e.jsx("div",{className:"flex flex-col gap-4 py-4",children:e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("code",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"master_key"}),e.jsx("p",{className:"mt-1 max-w-3xl text-sm text-[var(--otari-muted)]",children:o?"This gateway uses its first-run generated dashboard key. Regeneration invalidates the current key immediately.":"This gateway uses a key managed through OTARI_MASTER_KEY or config.yml. Rotate it in configuration, then restart the gateway."})]}),e.jsxs(d,{isOpen:s,onOpenChange:m,children:[o?e.jsx(d.Trigger,{className:K({size:"sm",variant:"danger-soft"}),children:"Regenerate"}):e.jsx(h,{size:"sm",variant:"danger-soft",isDisabled:!0,children:"Managed in configuration"}),s?e.jsx(G,{masterKey:n,error:r.error,isPending:r.isPending,onRegenerate:c,onClose:p}):null]})]})})}function X(){var o;const t=F(),r=I(),s=r.data,a=((o=t.data)==null?void 0:o.length)??0,n=(t.data??[]).filter(c=>!c.decryptable).length,i=a>0;return e.jsxs("div",{className:"flex flex-col gap-4 py-4",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("code",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"OTARI_SECRET_KEY"}),e.jsxs("p",{className:"mt-1 max-w-3xl text-sm text-[var(--otari-muted)]",children:["Generate a new key with ",e.jsx("code",{children:"uv run otari gen-secret-key"}),", then restart with"," ",e.jsx("code",{children:"OTARI_SECRET_KEY=,"}),". Re-encrypt the stored provider keys, then restart with ",e.jsx("code",{children:"OTARI_SECRET_KEY="})," once none are unreadable."]})]}),e.jsx("div",{className:"shrink-0",children:e.jsx(h,{size:"sm",variant:"outline",isDisabled:!i||r.isPending,onPress:()=>r.mutate(),children:r.isPending?"Re-encrypting…":"Re-encrypt provider keys"})})]}),e.jsx(y,{error:t.error??r.error}),n>0?e.jsxs(w,{tone:"warning",children:[n," stored provider key",n===1?"":"s"," cannot be decrypted with the current"," ",e.jsx("code",{children:"OTARI_SECRET_KEY"}),". Restore the old secret key and re-encrypt, or edit each affected provider and replace its key."]}):null,s?e.jsxs("p",{className:"text-sm text-[var(--otari-muted)]",role:"status","aria-live":"polite",children:["Re-encrypted ",s.reencrypted," provider key",s.reencrypted===1?"":"s",".",s.unreadable>0?` ${s.unreadable} still need replacement.`:" All decryptable stored keys now use the primary secret key."]}):!t.isLoading&&!i?e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:"No stored provider keys need re-encryption."}):null]})}function J({masterKeySource:t}){return e.jsxs("section",{className:"flex flex-col gap-2",children:[e.jsxs("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:["Credential security ",e.jsx("span",{className:"font-normal text-[var(--otari-muted)]",children:"(2)"})]}),e.jsx(v,{children:e.jsxs(v.Content,{className:"flex flex-col divide-y divide-[var(--otari-line)] px-5 py-1",children:[e.jsx(Q,{source:t}),e.jsx(X,{})]})})]})}function W({preview:t,error:r,isPending:s,onAccept:a,onReject:n}){return e.jsx(d.Backdrop,{children:e.jsx(d.Container,{placement:"center",size:"lg",children:e.jsxs(d.Dialog,{children:[e.jsx(d.Header,{children:e.jsx(d.Heading,{children:"Review default price updates"})}),e.jsxs(d.Body,{className:"flex flex-col gap-4",children:[e.jsxs("p",{className:"text-sm text-[var(--otari-muted)]",children:[t.added_count," added, ",t.changed_count," changed, and ",t.removed_count," removed upstream model prices. The accepted catalog is saved in the database with source ",e.jsx("code",{children:"genai-prices"})," and reloads after a restart. Your ",t.protected_model_count," custom model price",t.protected_model_count===1?"":"s"," remain unchanged."]}),t.changes.length>0?e.jsx("ul",{className:"max-h-60 list-disc overflow-auto pl-5 text-sm text-[var(--otari-ink)]",children:t.changes.map(i=>e.jsxs("li",{children:[i.model_key,": ",i.change]},i.model_key))}):null,t.changes_truncated?e.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:"Only the first 100 changes are shown."}):null,e.jsx(y,{error:r})]}),e.jsxs(d.Footer,{children:[e.jsx(h,{variant:"ghost",isDisabled:s,onPress:n,children:"Reject changes"}),e.jsx(h,{variant:"primary",isPending:s,onPress:a,children:"Accept price updates"})]})]})})})}function Z(){const t=T(),r=_(),s=D(),a=t.data,n=r.isPending||s.isPending,i=()=>{a===void 0||n||s.mutate(void 0,{onSuccess:t.reset})};return e.jsxs("section",{className:"flex flex-col gap-2",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Default pricing catalog"}),e.jsx(v,{children:e.jsxs(v.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"genai-prices defaults"}),e.jsxs("p",{className:"mt-1 max-w-3xl text-sm text-[var(--otari-muted)]",children:["Fetch the latest upstream catalog, review the proposed change summary, then accept or reject it. Accepted data is stored as ",e.jsx("code",{children:"genai-prices"}),"; custom prices remain separate and always take precedence."]})]}),e.jsx(h,{size:"sm",variant:"outline",isDisabled:t.isPending||n,onPress:()=>t.mutate(),children:t.isPending?"Checking prices…":"Check for price updates"})]}),e.jsx(y,{error:t.error})]})}),e.jsxs(d,{isOpen:a!==void 0,onOpenChange:o=>o?void 0:i(),children:[e.jsx(d.Trigger,{className:"hidden",children:"Review price updates"}),a?e.jsx(W,{preview:a,error:r.error??s.error,isPending:n,onAccept:()=>r.mutate(void 0,{onSuccess:t.reset}),onReject:i}):null]})]})}function ee(t){const r=[],s=new Map;for(const a of t){let n=s.get(a.group);n||(n={name:a.group,fields:[]},s.set(a.group,n),r.push(n)),n.fields.push(a)}return r}function ne(){const t=R(),r=C(),s=t.data,a=r.isPending,[n,i]=u.useState(""),[o,c]=u.useState(!1),p=u.useRef(null);u.useEffect(()=>{function l(f){var N;const j=f.target,S=j&&(j.tagName==="INPUT"||j.tagName==="TEXTAREA"||j.tagName==="SELECT");f.key==="/"&&!S&&(f.preventDefault(),(N=p.current)==null||N.focus())}return window.addEventListener("keydown",l),()=>window.removeEventListener("keydown",l)},[]);const m=l=>r.mutate(l),x=(s==null?void 0:s.config)??[],g=x.filter(l=>(o?l.settable:!0)&&L(l,n)),k=ee(g);return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(P,{title:"Settings",description:"Every effective gateway setting. Settable fields apply immediately and persist across restarts; startup-only fields are shown for reference and change only via config.yml or environment variables (then a restart)."}),e.jsx(y,{error:t.error??r.error}),e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("input",{ref:p,type:"search","aria-label":"Search settings",placeholder:"Search settings (press / to focus)…",value:n,onChange:l=>i(l.target.value),onKeyDown:l=>{l.key==="Escape"&&i("")},className:"min-w-0 flex-1 rounded-lg border border-[var(--otari-line)] bg-[var(--otari-bg)] px-3 py-2 text-sm text-[var(--otari-ink)] focus:border-[var(--otari-brand)] focus:outline-none"}),e.jsxs("label",{className:"flex items-center gap-2 text-sm text-[var(--otari-muted)]",children:[e.jsx("input",{type:"checkbox",checked:o,onChange:l=>c(l.target.checked),className:"h-4 w-4 accent-[var(--otari-brand)]"}),"Settable only"]})]}),s?e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["Showing ",g.length," of ",x.length," settings"]}):null,s&&g.length===0?e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:"No settings match your search."}):null,t.isLoading?e.jsx(E,{}):null,k.map(l=>e.jsxs("section",{className:"flex flex-col gap-2",children:[e.jsxs("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:[l.name," ",e.jsxs("span",{className:"font-normal text-[var(--otari-muted)]",children:["(",l.fields.length,")"]})]}),e.jsx(v,{children:e.jsx(v.Content,{className:"flex flex-col divide-y divide-[var(--otari-line)] px-5 py-1",children:l.fields.map(f=>e.jsx(U,{field:f,patch:m,disabled:!s||a},f.key))})})]},l.name)),s?e.jsx(Z,{}):null,s?e.jsx(J,{masterKeySource:s.master_key_source}):null,s?e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["Mode: ",s.mode," · Version ",s.version,s.require_pricing?" · require_pricing on":""]}):null]})}export{ne as SettingsPage,L as fieldMatches}; diff --git a/src/gateway/static/dashboard/assets/SettingsPage-DR0J2MHH.js b/src/gateway/static/dashboard/assets/SettingsPage-DR0J2MHH.js deleted file mode 100644 index f2eb5fab..00000000 --- a/src/gateway/static/dashboard/assets/SettingsPage-DR0J2MHH.js +++ /dev/null @@ -1 +0,0 @@ -import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as u}from"./react-dgEcD0HR.js";import{J as R,_ as C,P,E as y,M as E,a5 as T,a6 as _,a7 as D,F as A,a8 as O,X as F,a9 as I,I as w}from"./index-DV8qvdUj.js";import{d as v,B as h,A as d,g as K,I as M}from"./heroui-BI50yK5B.js";function b(t,r){return{[t]:r}}function z(t,r){let s=0;for(const a of r)if(a===t[s]&&(s+=1),s===t.length)return!0;return t.length===0}function L(t,r){const s=r.trim().toLowerCase();if(s==="")return!0;const a=`${t.key} ${t.description??""} ${t.group}`.toLowerCase(),n=t.key.toLowerCase().replace(/[^a-z0-9]/g,"");return s.split(/\s+/).every(i=>a.includes(i)||z(i,n))}function B({checked:t,onChange:r,label:s,disabled:a}){return e.jsx("button",{type:"button",role:"switch","aria-checked":t,"aria-label":s,disabled:a,onClick:()=>r(!t),className:`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors disabled:opacity-50 ${t?"bg-[var(--otari-brand)]":"bg-[var(--otari-line)]"}`,children:e.jsx("span",{className:`inline-block h-5 w-5 transform rounded-full bg-white shadow transition-transform ${t?"translate-x-5":"translate-x-0.5"}`})})}function $({field:t,onSave:r,disabled:s}){const a=typeof t.value=="number"?t.value:0,[n,i]=u.useState(String(a)),o=t.type==="float";u.useEffect(()=>{i(String(a))},[a]);const c=Number(n),p=n.trim()!==""&&Number.isFinite(c)&&(o||Number.isInteger(c)),m=t.minimum??void 0,x=t.exclusive_minimum??void 0,g=x!==void 0?c>x:m!==void 0?c>=m:c>=0,l=p&&g&&c!==a;return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(M,{type:"number",min:"0",step:o?"any":"1",inputMode:o?"decimal":"numeric","aria-label":t.key,value:n,disabled:s,onChange:f=>i(f.target.value),className:"w-28 rounded-md border border-[var(--otari-line)] bg-white px-2 py-1 text-right text-sm tabular-nums focus:border-[var(--otari-brand)] focus:outline-none disabled:opacity-50"}),e.jsx(h,{size:"sm",variant:"primary","aria-label":`Save ${t.key}`,isDisabled:s||!l,onPress:()=>r(c),children:"Save"})]})}function H({field:t,onSave:r,disabled:s}){const a=typeof t.value=="string"?t.value:"",[n,i]=u.useState(a);u.useEffect(()=>{i(a)},[a]);const o=n!==a;return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"text","aria-label":t.key,value:n,disabled:s,placeholder:"unset",onChange:c=>i(c.target.value),className:"w-56 rounded-md border border-[var(--otari-line)] bg-white px-2 py-1 text-sm focus:border-[var(--otari-brand)] focus:outline-none disabled:opacity-50"}),e.jsx(h,{size:"sm",variant:"primary","aria-label":`Save ${t.key}`,isDisabled:s||!o,onPress:()=>r(n.trim()===""?null:n),children:"Save"})]})}function Y(t){const{value:r}=t;return r==null?"unset":typeof r=="boolean"?r?"on":"off":Array.isArray(r)?r.length?r.join(", "):"none":String(r)}function q({field:t,patch:r,disabled:s}){return t.settable?t.type==="bool"?e.jsx(B,{checked:t.value===!0,onChange:a=>r(b(t.key,a)),label:t.key,disabled:s}):t.options&&t.options.length>0?e.jsx(A,{ariaLabel:t.key,value:String(t.value??""),onChange:a=>r(b(t.key,a)),options:t.options.map(a=>({value:a,label:a}))}):t.type==="int"||t.type==="float"?e.jsx($,{field:t,onSave:a=>r(b(t.key,a)),disabled:s}):e.jsx(H,{field:t,onSave:a=>r(b(t.key,a)),disabled:s}):e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-sm tabular-nums text-[var(--otari-ink)]",children:Y(t)}),e.jsx("span",{className:"rounded-full border border-[var(--otari-line)] px-2 py-0.5 text-xs text-[var(--otari-muted)]",children:"startup-only"})]})}function U({field:t,patch:r,disabled:s}){return e.jsxs("div",{className:"flex items-start justify-between gap-6 py-4",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("code",{className:"text-sm font-medium text-[var(--otari-ink)]",children:t.key}),t.description?e.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:t.description}):null]}),e.jsx("div",{className:"shrink-0 pt-0.5",children:e.jsx(q,{field:t,patch:r,disabled:s})})]})}function V({value:t,fieldRef:r}){const s=u.useRef(null),a=r??s,[n,i]=u.useState(!1),[o,c]=u.useState(!1),p=async()=>{var m,x,g;(m=a.current)==null||m.focus(),(x=a.current)==null||x.select();try{if((g=navigator.clipboard)!=null&&g.writeText){await navigator.clipboard.writeText(t),i(!0),c(!1),window.setTimeout(()=>i(!1),2e3);return}}catch{}c(!0)};return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-xs font-medium text-[var(--otari-muted)]",children:"New master key"}),e.jsx(h,{size:"sm",variant:"outline",onPress:p,children:n?"Copied":"Copy"})]}),e.jsx("input",{ref:a,readOnly:!0,value:t,onFocus:m=>m.currentTarget.select(),autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",spellCheck:!1,"data-1p-ignore":!0,"data-lpignore":"true"}),e.jsx("span",{"aria-live":"polite",className:"text-xs text-[var(--otari-brand-dark)]",children:n?"Copied to clipboard.":""}),o?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"Selected. Press Ctrl/Cmd-C to copy."}):null]})}function G({masterKey:t,error:r,isPending:s,onRegenerate:a,onClose:n}){const i=u.useRef(null);return u.useEffect(()=>{var o,c;t!==void 0&&((o=i.current)==null||o.focus(),(c=i.current)==null||c.select())},[t]),e.jsx(d.Backdrop,{children:e.jsx(d.Container,{placement:"center",size:"lg",children:e.jsxs(d.Dialog,{children:[e.jsx(d.Header,{children:e.jsx(d.Heading,{children:t!==void 0?"Master key regenerated":"Regenerate master key?"})}),e.jsx(d.Body,{className:"flex flex-col gap-4",children:t!==void 0?e.jsxs(e.Fragment,{children:[e.jsx(w,{tone:"warning",children:"Copy this key now. It is shown once and cannot be retrieved again after you close this dialog."}),e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:"The previous master key has stopped working. This browser tab now uses the new key."}),e.jsx(V,{value:t,fieldRef:i})]}):e.jsxs(e.Fragment,{children:[e.jsx(w,{tone:"warning",children:"This immediately invalidates the current dashboard master key. Other signed-in dashboard sessions will need the new key to continue."}),e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:"The replacement key will be shown once. Save it before closing the next screen."}),e.jsx(y,{error:r})]})}),e.jsx(d.Footer,{children:t!==void 0?e.jsx(h,{variant:"primary",onPress:n,children:"I’ve saved this key"}):e.jsxs(e.Fragment,{children:[e.jsx(h,{variant:"ghost",isDisabled:s,onPress:n,children:"Cancel"}),e.jsx(h,{variant:"danger",isPending:s,onPress:a,children:"Regenerate key"})]})})]})})})}function X({source:t}){const r=O(),[s,a]=u.useState(!1),[n,i]=u.useState(),o=t==="generated",c=()=>r.mutate(void 0,{onSuccess:x=>{i(x.master_key)}}),p=()=>{a(!1),i(void 0),r.reset()},m=x=>{x?(r.reset(),a(!0)):n===void 0&&p()};return e.jsx("div",{className:"flex flex-col gap-4 py-4",children:e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("code",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"master_key"}),e.jsx("p",{className:"mt-1 max-w-3xl text-sm text-[var(--otari-muted)]",children:o?"This gateway uses its first-run generated dashboard key. Regeneration invalidates the current key immediately.":"This gateway uses a key managed through OTARI_MASTER_KEY or config.yml. Rotate it in configuration, then restart the gateway."})]}),e.jsxs(d,{isOpen:s,onOpenChange:m,children:[o?e.jsx(d.Trigger,{className:K({size:"sm",variant:"danger-soft"}),children:"Regenerate"}):e.jsx(h,{size:"sm",variant:"danger-soft",isDisabled:!0,children:"Managed in configuration"}),s?e.jsx(G,{masterKey:n,error:r.error,isPending:r.isPending,onRegenerate:c,onClose:p}):null]})]})})}function J(){var o;const t=F(),r=I(),s=r.data,a=((o=t.data)==null?void 0:o.length)??0,n=(t.data??[]).filter(c=>!c.decryptable).length,i=a>0;return e.jsxs("div",{className:"flex flex-col gap-4 py-4",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("code",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"OTARI_SECRET_KEY"}),e.jsxs("p",{className:"mt-1 max-w-3xl text-sm text-[var(--otari-muted)]",children:["Generate a new key with ",e.jsx("code",{children:"uv run otari gen-secret-key"}),", then restart with"," ",e.jsx("code",{children:"OTARI_SECRET_KEY=,"}),". Re-encrypt the stored provider keys, then restart with ",e.jsx("code",{children:"OTARI_SECRET_KEY="})," once none are unreadable."]})]}),e.jsx("div",{className:"shrink-0",children:e.jsx(h,{size:"sm",variant:"outline",isDisabled:!i||r.isPending,onPress:()=>r.mutate(),children:r.isPending?"Re-encrypting…":"Re-encrypt provider keys"})})]}),e.jsx(y,{error:t.error??r.error}),n>0?e.jsxs(w,{tone:"warning",children:[n," stored provider key",n===1?"":"s"," cannot be decrypted with the current"," ",e.jsx("code",{children:"OTARI_SECRET_KEY"}),". Restore the old secret key and re-encrypt, or edit each affected provider and replace its key."]}):null,s?e.jsxs("p",{className:"text-sm text-[var(--otari-muted)]",role:"status","aria-live":"polite",children:["Re-encrypted ",s.reencrypted," provider key",s.reencrypted===1?"":"s",".",s.unreadable>0?` ${s.unreadable} still need replacement.`:" All decryptable stored keys now use the primary secret key."]}):!t.isLoading&&!i?e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:"No stored provider keys need re-encryption."}):null]})}function Q({masterKeySource:t}){return e.jsxs("section",{className:"flex flex-col gap-2",children:[e.jsxs("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:["Credential security ",e.jsx("span",{className:"font-normal text-[var(--otari-muted)]",children:"(2)"})]}),e.jsx(v,{children:e.jsxs(v.Content,{className:"flex flex-col divide-y divide-[var(--otari-line)] px-5 py-1",children:[e.jsx(X,{source:t}),e.jsx(J,{})]})})]})}function W({preview:t,error:r,isPending:s,onAccept:a,onReject:n}){return e.jsx(d.Backdrop,{children:e.jsx(d.Container,{placement:"center",size:"lg",children:e.jsxs(d.Dialog,{children:[e.jsx(d.Header,{children:e.jsx(d.Heading,{children:"Review default price updates"})}),e.jsxs(d.Body,{className:"flex flex-col gap-4",children:[e.jsxs("p",{className:"text-sm text-[var(--otari-muted)]",children:[t.added_count," added, ",t.changed_count," changed, and ",t.removed_count," removed upstream model prices. The accepted catalog is saved in the database with source ",e.jsx("code",{children:"genai-prices"})," and reloads after a restart. Your ",t.protected_model_count," custom model price",t.protected_model_count===1?"":"s"," remain unchanged."]}),t.changes.length>0?e.jsx("ul",{className:"max-h-60 list-disc overflow-auto pl-5 text-sm text-[var(--otari-ink)]",children:t.changes.map(i=>e.jsxs("li",{children:[i.model_key,": ",i.change]},i.model_key))}):null,t.changes_truncated?e.jsx("p",{className:"text-xs text-[var(--otari-muted)]",children:"Only the first 100 changes are shown."}):null,e.jsx(y,{error:r})]}),e.jsxs(d.Footer,{children:[e.jsx(h,{variant:"ghost",isDisabled:s,onPress:n,children:"Reject changes"}),e.jsx(h,{variant:"primary",isPending:s,onPress:a,children:"Accept price updates"})]})]})})})}function Z(){const t=T(),r=_(),s=D(),a=t.data,n=r.isPending||s.isPending,i=()=>{a===void 0||n||s.mutate(void 0,{onSuccess:t.reset})};return e.jsxs("section",{className:"flex flex-col gap-2",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Default pricing catalog"}),e.jsx(v,{children:e.jsxs(v.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-sm font-medium text-[var(--otari-ink)]",children:"genai-prices defaults"}),e.jsxs("p",{className:"mt-1 max-w-3xl text-sm text-[var(--otari-muted)]",children:["Fetch the latest upstream catalog, review the proposed change summary, then accept or reject it. Accepted data is stored as ",e.jsx("code",{children:"genai-prices"}),"; custom prices remain separate and always take precedence."]})]}),e.jsx(h,{size:"sm",variant:"outline",isDisabled:t.isPending||n,onPress:()=>t.mutate(),children:t.isPending?"Checking prices…":"Check for price updates"})]}),e.jsx(y,{error:t.error})]})}),e.jsxs(d,{isOpen:a!==void 0,onOpenChange:o=>o?void 0:i(),children:[e.jsx(d.Trigger,{className:"hidden",children:"Review price updates"}),a?e.jsx(W,{preview:a,error:r.error??s.error,isPending:n,onAccept:()=>r.mutate(void 0,{onSuccess:t.reset}),onReject:i}):null]})]})}function ee(t){const r=[],s=new Map;for(const a of t){let n=s.get(a.group);n||(n={name:a.group,fields:[]},s.set(a.group,n),r.push(n)),n.fields.push(a)}return r}function ne(){const t=R(),r=C(),s=t.data,a=r.isPending,[n,i]=u.useState(""),[o,c]=u.useState(!1),p=u.useRef(null);u.useEffect(()=>{function l(f){var N;const j=f.target,S=j&&(j.tagName==="INPUT"||j.tagName==="TEXTAREA"||j.tagName==="SELECT");f.key==="/"&&!S&&(f.preventDefault(),(N=p.current)==null||N.focus())}return window.addEventListener("keydown",l),()=>window.removeEventListener("keydown",l)},[]);const m=l=>r.mutate(l),x=(s==null?void 0:s.config)??[],g=x.filter(l=>(o?l.settable:!0)&&L(l,n)),k=ee(g);return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(P,{title:"Settings",description:"Every effective gateway setting. Settable fields apply immediately and persist across restarts; startup-only fields are shown for reference and change only via config.yml or environment variables (then a restart)."}),e.jsx(y,{error:t.error??r.error}),e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("input",{ref:p,type:"search","aria-label":"Search settings",placeholder:"Search settings (press / to focus)…",value:n,onChange:l=>i(l.target.value),onKeyDown:l=>{l.key==="Escape"&&i("")},className:"min-w-0 flex-1 rounded-lg border border-[var(--otari-line)] bg-[var(--otari-bg)] px-3 py-2 text-sm text-[var(--otari-ink)] focus:border-[var(--otari-brand)] focus:outline-none"}),e.jsxs("label",{className:"flex items-center gap-2 text-sm text-[var(--otari-muted)]",children:[e.jsx("input",{type:"checkbox",checked:o,onChange:l=>c(l.target.checked),className:"h-4 w-4 accent-[var(--otari-brand)]"}),"Settable only"]})]}),s?e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["Showing ",g.length," of ",x.length," settings"]}):null,s&&g.length===0?e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:"No settings match your search."}):null,t.isLoading?e.jsx(E,{}):null,k.map(l=>e.jsxs("section",{className:"flex flex-col gap-2",children:[e.jsxs("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:[l.name," ",e.jsxs("span",{className:"font-normal text-[var(--otari-muted)]",children:["(",l.fields.length,")"]})]}),e.jsx(v,{children:e.jsx(v.Content,{className:"flex flex-col divide-y divide-[var(--otari-line)] px-5 py-1",children:l.fields.map(f=>e.jsx(U,{field:f,patch:m,disabled:!s||a},f.key))})})]},l.name)),s?e.jsx(Z,{}):null,s?e.jsx(Q,{masterKeySource:s.master_key_source}):null,s?e.jsxs("p",{className:"text-xs text-[var(--otari-muted)]",children:["Mode: ",s.mode," · Version ",s.version,s.require_pricing?" · require_pricing on":""]}):null]})}export{ne as SettingsPage,L as fieldMatches}; diff --git a/src/gateway/static/dashboard/assets/TablePagination-CXhesdmH.js b/src/gateway/static/dashboard/assets/TablePagination-BR55oZ8k.js similarity index 98% rename from src/gateway/static/dashboard/assets/TablePagination-CXhesdmH.js rename to src/gateway/static/dashboard/assets/TablePagination-BR55oZ8k.js index 17fa3207..e11b7fbb 100644 --- a/src/gateway/static/dashboard/assets/TablePagination-CXhesdmH.js +++ b/src/gateway/static/dashboard/assets/TablePagination-BR55oZ8k.js @@ -1 +1 @@ -import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as u}from"./react-dgEcD0HR.js";import{I as w,E as M,F as D}from"./index-DV8qvdUj.js";import{A as o,B as x,T as F,L as $,I as T,S as y}from"./heroui-BI50yK5B.js";function _({label:t,value:r,onChange:a,isRequired:c,autoFocus:i}){return e.jsxs(F,{value:r,onChange:a,isRequired:c,className:"flex flex-col gap-1",children:[e.jsx($,{className:"text-sm font-medium text-[var(--otari-ink)]",children:t}),e.jsx(T,{inputMode:"decimal",placeholder:"0.00",autoFocus:i})]})}function R(t){const r=t.trim();if(r==="")return null;const a=Number(r);return Number.isFinite(a)&&a>=0?a:Number.NaN}const C=t=>`Recompute cost for ${t.toLocaleString()} imported ${t===1?"row":"rows"} from each row's own token counts at these per-1M rates. Enforced gateway rows are never affected.`;function W({isOpen:t,onOpenChange:r,targetCount:a,isPending:c,error:i,onSubmit:E,title:I="Set price",description:L=C}){const[j,p]=u.useState(""),[n,h]=u.useState(""),[b,N]=u.useState(""),[f,v]=u.useState("");u.useEffect(()=>{t&&(p(""),h(""),N(""),v(""))},[t]);const m=R(j),l=R(n),d=R(b),s=R(f),g=m===null||Number.isNaN(m)||l===null||Number.isNaN(l)||Number.isNaN(d??0)||Number.isNaN(s??0),S=()=>{g||m===null||l===null||E({input_price_per_million:m,output_price_per_million:l,...d!==null&&!Number.isNaN(d)?{cache_read_price_per_million:d}:{},...s!==null&&!Number.isNaN(s)?{cache_write_price_per_million:s}:{}})};return e.jsx(o,{isOpen:t,onOpenChange:r,children:t?e.jsx(o.Backdrop,{children:e.jsx(o.Container,{placement:"center",size:"lg",children:e.jsxs(o.Dialog,{children:[e.jsx(o.Header,{children:e.jsx(o.Heading,{children:I})}),e.jsxs(o.Body,{className:"flex flex-col gap-4",children:[e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:L(a)}),e.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[e.jsx(_,{label:"Input $ / 1M",value:j,onChange:p,isRequired:!0,autoFocus:!0}),e.jsx(_,{label:"Output $ / 1M",value:n,onChange:h,isRequired:!0}),e.jsx(_,{label:"Cache read $ / 1M",value:b,onChange:N}),e.jsx(_,{label:"Cache write $ / 1M",value:f,onChange:v})]}),e.jsx(w,{tone:"info",children:"Leave a cache rate blank to bill those tokens at the input rate."}),e.jsx(M,{error:i})]}),e.jsxs(o.Footer,{children:[e.jsx(x,{variant:"ghost",isDisabled:c,onPress:()=>r(!1),children:"Cancel"}),e.jsx(x,{variant:"primary",isDisabled:g,isPending:c,onPress:S,children:"Set price"})]})]})})}):null})}const P=[25,50,100];function q({page:t,pageSize:r,total:a,rowsOnPage:c,onPageChange:i,onPageSizeChange:E,pageSizeOptions:I=P,isFetching:L=!1,hasNextFallback:j=!1}){const p=u.useId(),n=a!=null?Math.max(1,Math.ceil(a/r)):null,h=t===0,b=n!=null?t>=n-1:!j,N=c>0?t*r+1:0,f=t*r+c,v=a!=null?a===0?"0 of 0":`${N.toLocaleString()}–${f.toLocaleString()} of ${a.toLocaleString()}`:c>0?`${N.toLocaleString()}–${f.toLocaleString()}`:"0",[m,l]=u.useState(String(t+1));u.useEffect(()=>{l(String(t+1))},[t]);const d=()=>{const s=Number.parseInt(m,10);if(Number.isNaN(s)){l(String(t+1));return}const g=n??Number.MAX_SAFE_INTEGER,S=Math.min(Math.max(s,1),g);S-1!==t?i(S-1):l(String(t+1))};return e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("label",{htmlFor:p,className:"text-sm text-[var(--otari-muted)]",children:"Rows"}),e.jsx(D,{id:p,ariaLabel:"Rows per page",value:String(r),onChange:s=>E(Number.parseInt(s,10)),options:I.map(s=>({value:String(s),label:String(s)}))}),L?e.jsx(y,{size:"sm"}):null]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"text-sm text-[var(--otari-muted)] tabular-nums",children:v}),e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(x,{size:"sm",variant:"outline","aria-label":"First page",isDisabled:h,onPress:()=>i(0),children:"«"}),e.jsx(x,{size:"sm",variant:"outline","aria-label":"Previous page",isDisabled:h,onPress:()=>i(t-1),children:"‹"}),e.jsxs("span",{className:"inline-flex items-center gap-1 text-sm text-[var(--otari-muted)]",children:[e.jsx("input",{"aria-label":"Page number",inputMode:"numeric",value:m,onChange:s=>l(s.target.value.replace(/[^0-9]/g,"")),onKeyDown:s=>{s.key==="Enter"&&s.currentTarget.blur()},onBlur:d,className:"w-12 rounded-lg border border-[var(--otari-line)] bg-[var(--otari-bg)] px-2 py-1 text-center text-sm text-[var(--otari-ink)] tabular-nums focus:border-[var(--otari-brand)] focus:outline-none"}),n!=null?e.jsxs("span",{className:"tabular-nums",children:["/ ",n.toLocaleString()]}):null]}),e.jsx(x,{size:"sm",variant:"outline","aria-label":"Next page",isDisabled:b,onPress:()=>i(t+1),children:"›"}),e.jsx(x,{size:"sm",variant:"outline","aria-label":"Last page",isDisabled:n==null||b,onPress:()=>n!=null&&i(n-1),children:"»"})]})]})]})}export{P,W as S,q as T}; +import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as u}from"./react-dgEcD0HR.js";import{I as w,E as M,F as D}from"./index-Br3lxdY5.js";import{A as o,B as x,T as F,L as $,I as T,S as y}from"./heroui-BI50yK5B.js";function _({label:t,value:r,onChange:a,isRequired:c,autoFocus:i}){return e.jsxs(F,{value:r,onChange:a,isRequired:c,className:"flex flex-col gap-1",children:[e.jsx($,{className:"text-sm font-medium text-[var(--otari-ink)]",children:t}),e.jsx(T,{inputMode:"decimal",placeholder:"0.00",autoFocus:i})]})}function R(t){const r=t.trim();if(r==="")return null;const a=Number(r);return Number.isFinite(a)&&a>=0?a:Number.NaN}const C=t=>`Recompute cost for ${t.toLocaleString()} imported ${t===1?"row":"rows"} from each row's own token counts at these per-1M rates. Enforced gateway rows are never affected.`;function W({isOpen:t,onOpenChange:r,targetCount:a,isPending:c,error:i,onSubmit:E,title:I="Set price",description:L=C}){const[j,p]=u.useState(""),[n,h]=u.useState(""),[b,N]=u.useState(""),[f,v]=u.useState("");u.useEffect(()=>{t&&(p(""),h(""),N(""),v(""))},[t]);const m=R(j),l=R(n),d=R(b),s=R(f),g=m===null||Number.isNaN(m)||l===null||Number.isNaN(l)||Number.isNaN(d??0)||Number.isNaN(s??0),S=()=>{g||m===null||l===null||E({input_price_per_million:m,output_price_per_million:l,...d!==null&&!Number.isNaN(d)?{cache_read_price_per_million:d}:{},...s!==null&&!Number.isNaN(s)?{cache_write_price_per_million:s}:{}})};return e.jsx(o,{isOpen:t,onOpenChange:r,children:t?e.jsx(o.Backdrop,{children:e.jsx(o.Container,{placement:"center",size:"lg",children:e.jsxs(o.Dialog,{children:[e.jsx(o.Header,{children:e.jsx(o.Heading,{children:I})}),e.jsxs(o.Body,{className:"flex flex-col gap-4",children:[e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:L(a)}),e.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[e.jsx(_,{label:"Input $ / 1M",value:j,onChange:p,isRequired:!0,autoFocus:!0}),e.jsx(_,{label:"Output $ / 1M",value:n,onChange:h,isRequired:!0}),e.jsx(_,{label:"Cache read $ / 1M",value:b,onChange:N}),e.jsx(_,{label:"Cache write $ / 1M",value:f,onChange:v})]}),e.jsx(w,{tone:"info",children:"Leave a cache rate blank to bill those tokens at the input rate."}),e.jsx(M,{error:i})]}),e.jsxs(o.Footer,{children:[e.jsx(x,{variant:"ghost",isDisabled:c,onPress:()=>r(!1),children:"Cancel"}),e.jsx(x,{variant:"primary",isDisabled:g,isPending:c,onPress:S,children:"Set price"})]})]})})}):null})}const P=[25,50,100];function q({page:t,pageSize:r,total:a,rowsOnPage:c,onPageChange:i,onPageSizeChange:E,pageSizeOptions:I=P,isFetching:L=!1,hasNextFallback:j=!1}){const p=u.useId(),n=a!=null?Math.max(1,Math.ceil(a/r)):null,h=t===0,b=n!=null?t>=n-1:!j,N=c>0?t*r+1:0,f=t*r+c,v=a!=null?a===0?"0 of 0":`${N.toLocaleString()}–${f.toLocaleString()} of ${a.toLocaleString()}`:c>0?`${N.toLocaleString()}–${f.toLocaleString()}`:"0",[m,l]=u.useState(String(t+1));u.useEffect(()=>{l(String(t+1))},[t]);const d=()=>{const s=Number.parseInt(m,10);if(Number.isNaN(s)){l(String(t+1));return}const g=n??Number.MAX_SAFE_INTEGER,S=Math.min(Math.max(s,1),g);S-1!==t?i(S-1):l(String(t+1))};return e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("label",{htmlFor:p,className:"text-sm text-[var(--otari-muted)]",children:"Rows"}),e.jsx(D,{id:p,ariaLabel:"Rows per page",value:String(r),onChange:s=>E(Number.parseInt(s,10)),options:I.map(s=>({value:String(s),label:String(s)}))}),L?e.jsx(y,{size:"sm"}):null]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"text-sm text-[var(--otari-muted)] tabular-nums",children:v}),e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(x,{size:"sm",variant:"outline","aria-label":"First page",isDisabled:h,onPress:()=>i(0),children:"«"}),e.jsx(x,{size:"sm",variant:"outline","aria-label":"Previous page",isDisabled:h,onPress:()=>i(t-1),children:"‹"}),e.jsxs("span",{className:"inline-flex items-center gap-1 text-sm text-[var(--otari-muted)]",children:[e.jsx("input",{"aria-label":"Page number",inputMode:"numeric",value:m,onChange:s=>l(s.target.value.replace(/[^0-9]/g,"")),onKeyDown:s=>{s.key==="Enter"&&s.currentTarget.blur()},onBlur:d,className:"w-12 rounded-lg border border-[var(--otari-line)] bg-[var(--otari-bg)] px-2 py-1 text-center text-sm text-[var(--otari-ink)] tabular-nums focus:border-[var(--otari-brand)] focus:outline-none"}),n!=null?e.jsxs("span",{className:"tabular-nums",children:["/ ",n.toLocaleString()]}):null]}),e.jsx(x,{size:"sm",variant:"outline","aria-label":"Next page",isDisabled:b,onPress:()=>i(t+1),children:"›"}),e.jsx(x,{size:"sm",variant:"outline","aria-label":"Last page",isDisabled:n==null||b,onPress:()=>n!=null&&i(n-1),children:"»"})]})]})]})}export{P,W as S,q as T}; diff --git a/src/gateway/static/dashboard/assets/ToolsGuardrailsPage-DiZ1TbOa.js b/src/gateway/static/dashboard/assets/ToolsGuardrailsPage-B68S03Js.js similarity index 92% rename from src/gateway/static/dashboard/assets/ToolsGuardrailsPage-DiZ1TbOa.js rename to src/gateway/static/dashboard/assets/ToolsGuardrailsPage-B68S03Js.js index 2f90c443..2c6e6582 100644 --- a/src/gateway/static/dashboard/assets/ToolsGuardrailsPage-DiZ1TbOa.js +++ b/src/gateway/static/dashboard/assets/ToolsGuardrailsPage-B68S03Js.js @@ -1 +1 @@ -import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as x}from"./react-dgEcD0HR.js";import{aa as L,ab as C,P,E,M as R,G as w,ac as D,F as M}from"./index-DV8qvdUj.js";import{d as N,B as g}from"./heroui-BI50yK5B.js";function U(s,t){return{[s]:t}}const B=[{key:"web_search",label:"Web search",blurb:"Backend for otari_web_search tools (a SearXNG instance or a search adapter).",order:["web_search_url","web_search_engines","web_search_max_results","web_search_extract","web_search_purpose_hint"]},{key:"sandbox",label:"Code execution",blurb:"Backend for otari_code_execution tools (the sandbox that runs generated code).",order:["sandbox_url","sandbox_purpose_hint"]},{key:"guardrails",label:"Guardrails",blurb:"Default input-guardrails service used when a request does not pass its own guardrail URL.",order:["guardrails_url"]}];function $(){const[s,t]=x.useState(null),a=x.useRef(void 0),n=r=>{t(r),window.clearTimeout(a.current),a.current=window.setTimeout(()=>t(null),2500)};return x.useEffect(()=>()=>window.clearTimeout(a.current),[]),[s,n]}function G({message:s}){return s?e.jsxs("div",{role:"status","aria-live":"polite",className:"fixed right-4 bottom-4 z-50 flex items-center gap-2 rounded-lg border border-green-200 bg-green-50 px-4 py-3 text-sm font-medium text-green-700 shadow-lg",children:[e.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,className:"h-5 w-5",children:e.jsx("path",{d:"M20 6 9 17l-5-5",strokeLinecap:"round",strokeLinejoin:"round"})}),s]}):null}const S="rounded-md border border-[var(--otari-line)] bg-[var(--otari-surface)] px-2 py-1 text-sm focus:border-[var(--otari-brand)] focus:outline-none disabled:opacity-50",b="grid gap-x-4 gap-y-1.5 py-4 sm:grid-cols-[minmax(0,1fr)_16rem_10rem] sm:items-start",_=`w-full sm:col-start-2 ${S}`,k="flex items-center gap-2 sm:col-start-3",f="flex flex-col gap-1 sm:col-span-2 sm:col-start-2";function j({message:s}){return s?e.jsx("span",{className:"break-words text-xs text-red-700",children:s}):null}function y({field:s,help:t}){return e.jsxs("div",{className:"min-w-0 sm:col-start-1",children:[e.jsx("code",{className:"text-sm font-medium text-[var(--otari-ink)]",children:s.key}),s.description?e.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:s.description}):null,t?e.jsx("p",{className:"mt-1 text-xs text-[var(--otari-muted)]",children:t}):null]})}function z({field:s,onSave:t,saveError:a,disabled:n}){const r=typeof s.value=="string"?s.value:"",[l,i]=x.useState(r),[m,p]=x.useState(null),c=D();x.useEffect(()=>{i(r)},[r]);const o=l.trim()!==r,d=l.trim(),h=m!==null&&m===d;return e.jsxs("div",{className:b,children:[e.jsx(y,{field:s,help:"Leave blank and Save to fall back to the configured default."}),e.jsx("input",{type:"text",inputMode:"url","aria-label":s.key,value:l,disabled:n,placeholder:"unset",onChange:v=>{i(v.target.value),c.reset()},className:_}),e.jsxs("div",{className:k,children:[e.jsx(g,{size:"sm",variant:"primary","aria-label":`Save ${s.key}`,isDisabled:n||!o,onPress:()=>t(d===""?null:d),children:"Save"}),e.jsx(g,{size:"sm",variant:"outline","aria-label":`Test ${s.service}`,isDisabled:d===""||c.isPending,onPress:()=>{p(d),c.mutate({service:s.service,url:d})},children:c.isPending?"Testing…":"Test"})]}),e.jsxs("div",{className:f,children:[e.jsx("span",{role:"status","aria-live":"polite",className:"block break-words text-xs",children:c.isPending||!h?null:c.error?e.jsx("span",{className:"text-red-700",children:w(c.error)}):c.data?e.jsx("span",{className:c.data.ok?"font-medium text-green-700":"text-red-700",children:c.data.reason}):null}),e.jsx(j,{message:a})]})]})}function I({field:s,onSave:t,saveError:a,disabled:n}){const r=typeof s.value=="string"?s.value:"",[l,i]=x.useState(r);x.useEffect(()=>{i(r)},[r]);const m=l!==r;return e.jsxs("div",{className:b,children:[e.jsx(y,{field:s}),e.jsx("input",{type:"text","aria-label":s.key,value:l,disabled:n,placeholder:"default",onChange:p=>i(p.target.value),className:_}),e.jsx("div",{className:k,children:e.jsx(g,{size:"sm",variant:"primary","aria-label":`Save ${s.key}`,isDisabled:n||!m,onPress:()=>t(l.trim()===""?null:l.trim()),children:"Save"})}),a?e.jsx("div",{className:f,children:e.jsx(j,{message:a})}):null]})}function A({field:s,onSave:t,saveError:a,disabled:n}){const r=typeof s.value=="number"?String(s.value):"",[l,i]=x.useState(r);x.useEffect(()=>{i(r)},[r]);const m=l.trim(),p=Number(m),o=(m===""||Number.isInteger(p)&&p>=1)&&m!==r;return e.jsxs("div",{className:b,children:[e.jsx(y,{field:s,help:"Leave blank to use the backend default."}),e.jsx("input",{type:"number",min:"1",step:"1",inputMode:"numeric","aria-label":s.key,value:l,disabled:n,placeholder:"default",onChange:d=>i(d.target.value),className:`w-full text-right tabular-nums sm:col-start-2 sm:w-28 sm:justify-self-end ${S}`}),e.jsx("div",{className:k,children:e.jsx(g,{size:"sm",variant:"primary","aria-label":`Save ${s.key}`,isDisabled:n||!o,onPress:()=>t(m===""?null:p),children:"Save"})}),a?e.jsx("div",{className:f,children:e.jsx(j,{message:a})}):null]})}function F({field:s,onSave:t,saveError:a,disabled:n}){const r=s.value===!0?"on":s.value===!1?"off":"default";return e.jsxs("div",{className:b,children:[e.jsx(y,{field:s}),e.jsx("div",{className:"sm:col-start-2 sm:justify-self-start",children:e.jsx(M,{ariaLabel:s.key,value:r,onChange:l=>t(l==="default"?null:l==="on"),options:[{value:"default",label:"Default"},{value:"on",label:"On"},{value:"off",label:"Off"}],disabled:n})}),a?e.jsx("div",{className:f,children:e.jsx(j,{message:a})}):null]})}function O({field:s,onSave:t,saveError:a,disabled:n}){return s.type==="url"?e.jsx(z,{field:s,onSave:t,saveError:a,disabled:n}):s.type==="int"?e.jsx(A,{field:s,onSave:t,saveError:a,disabled:n}):s.type==="bool"?e.jsx(F,{field:s,onSave:t,saveError:a,disabled:n}):e.jsx(I,{field:s,onSave:t,saveError:a,disabled:n})}function V(){const s=L(),t=C(),[a,n]=$(),[r,l]=x.useState({}),i=s.data,m=!i||t.isPending,p=new Map(((i==null?void 0:i.fields)??[]).map(o=>[o.key,o])),c=(o,d)=>{l(h=>{const{[o.key]:v,...u}=h;return u}),t.mutate(U(o.key,d),{onSuccess:()=>n(`${o.key} saved`),onError:h=>l(v=>({...v,[o.key]:w(h)}))})};return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(P,{title:"Tools & Guardrails",description:"Configure the built-in tool and guardrail service endpoints without a restart. Changes apply immediately and persist. URLs are validated for shape (http/https) and can be tested for reachability before saving; the network-safety gates for these services live on the Settings page."}),e.jsx(E,{error:s.error}),s.isLoading?e.jsx(R,{}):null,B.map(o=>{const d=o.order.map(u=>p.get(u)).filter(u=>u!==void 0),h=((i==null?void 0:i.fields)??[]).filter(u=>u.service===o.key&&!o.order.includes(u.key)),v=[...d,...h];return v.length===0?null:e.jsxs("section",{className:"flex flex-col gap-2",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:o.label}),e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:o.blurb}),e.jsx(N,{children:e.jsx(N.Content,{className:"flex flex-col divide-y divide-[var(--otari-line)] px-5 py-1",children:v.map(u=>e.jsx(O,{field:u,onSave:T=>c(u,T),saveError:r[u.key],disabled:m},u.key))})})]},o.key)}),e.jsx(G,{message:a})]})}export{V as ToolsGuardrailsPage}; +import{j as e}from"./tanstack-query-1t81HyiD.js";import{r as x}from"./react-dgEcD0HR.js";import{ag as L,ah as C,P,E,U as R,N as w,ai as D,F as U}from"./index-Br3lxdY5.js";import{d as N,B as g}from"./heroui-BI50yK5B.js";function M(s,t){return{[s]:t}}const B=[{key:"web_search",label:"Web search",blurb:"Backend for otari_web_search tools (a SearXNG instance or a search adapter).",order:["web_search_url","web_search_engines","web_search_max_results","web_search_extract","web_search_purpose_hint"]},{key:"sandbox",label:"Code execution",blurb:"Backend for otari_code_execution tools (the sandbox that runs generated code).",order:["sandbox_url","sandbox_purpose_hint"]},{key:"guardrails",label:"Guardrails",blurb:"Default input-guardrails service used when a request does not pass its own guardrail URL.",order:["guardrails_url"]}];function $(){const[s,t]=x.useState(null),a=x.useRef(void 0),n=r=>{t(r),window.clearTimeout(a.current),a.current=window.setTimeout(()=>t(null),2500)};return x.useEffect(()=>()=>window.clearTimeout(a.current),[]),[s,n]}function z({message:s}){return s?e.jsxs("div",{role:"status","aria-live":"polite",className:"fixed right-4 bottom-4 z-50 flex items-center gap-2 rounded-lg border border-green-200 bg-green-50 px-4 py-3 text-sm font-medium text-green-700 shadow-lg",children:[e.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,className:"h-5 w-5",children:e.jsx("path",{d:"M20 6 9 17l-5-5",strokeLinecap:"round",strokeLinejoin:"round"})}),s]}):null}const S="rounded-md border border-[var(--otari-line)] bg-[var(--otari-surface)] px-2 py-1 text-sm focus:border-[var(--otari-brand)] focus:outline-none disabled:opacity-50",b="grid gap-x-4 gap-y-1.5 py-4 sm:grid-cols-[minmax(0,1fr)_16rem_10rem] sm:items-start",_=`w-full sm:col-start-2 ${S}`,k="flex items-center gap-2 sm:col-start-3",f="flex flex-col gap-1 sm:col-span-2 sm:col-start-2";function j({message:s}){return s?e.jsx("span",{className:"break-words text-xs text-red-700",children:s}):null}function y({field:s,help:t}){return e.jsxs("div",{className:"min-w-0 sm:col-start-1",children:[e.jsx("code",{className:"text-sm font-medium text-[var(--otari-ink)]",children:s.key}),s.description?e.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:s.description}):null,t?e.jsx("p",{className:"mt-1 text-xs text-[var(--otari-muted)]",children:t}):null]})}function G({field:s,onSave:t,saveError:a,disabled:n}){const r=typeof s.value=="string"?s.value:"",[l,i]=x.useState(r),[m,p]=x.useState(null),c=D();x.useEffect(()=>{i(r)},[r]);const o=l.trim()!==r,d=l.trim(),h=m!==null&&m===d;return e.jsxs("div",{className:b,children:[e.jsx(y,{field:s,help:"Leave blank and Save to fall back to the configured default."}),e.jsx("input",{type:"text",inputMode:"url","aria-label":s.key,value:l,disabled:n,placeholder:"unset",onChange:v=>{i(v.target.value),c.reset()},className:_}),e.jsxs("div",{className:k,children:[e.jsx(g,{size:"sm",variant:"primary","aria-label":`Save ${s.key}`,isDisabled:n||!o,onPress:()=>t(d===""?null:d),children:"Save"}),e.jsx(g,{size:"sm",variant:"outline","aria-label":`Test ${s.service}`,isDisabled:d===""||c.isPending,onPress:()=>{p(d),c.mutate({service:s.service,url:d})},children:c.isPending?"Testing…":"Test"})]}),e.jsxs("div",{className:f,children:[e.jsx("span",{role:"status","aria-live":"polite",className:"block break-words text-xs",children:c.isPending||!h?null:c.error?e.jsx("span",{className:"text-red-700",children:w(c.error)}):c.data?e.jsx("span",{className:c.data.ok?"font-medium text-green-700":"text-red-700",children:c.data.reason}):null}),e.jsx(j,{message:a})]})]})}function I({field:s,onSave:t,saveError:a,disabled:n}){const r=typeof s.value=="string"?s.value:"",[l,i]=x.useState(r);x.useEffect(()=>{i(r)},[r]);const m=l!==r;return e.jsxs("div",{className:b,children:[e.jsx(y,{field:s}),e.jsx("input",{type:"text","aria-label":s.key,value:l,disabled:n,placeholder:"default",onChange:p=>i(p.target.value),className:_}),e.jsx("div",{className:k,children:e.jsx(g,{size:"sm",variant:"primary","aria-label":`Save ${s.key}`,isDisabled:n||!m,onPress:()=>t(l.trim()===""?null:l.trim()),children:"Save"})}),a?e.jsx("div",{className:f,children:e.jsx(j,{message:a})}):null]})}function A({field:s,onSave:t,saveError:a,disabled:n}){const r=typeof s.value=="number"?String(s.value):"",[l,i]=x.useState(r);x.useEffect(()=>{i(r)},[r]);const m=l.trim(),p=Number(m),o=(m===""||Number.isInteger(p)&&p>=1)&&m!==r;return e.jsxs("div",{className:b,children:[e.jsx(y,{field:s,help:"Leave blank to use the backend default."}),e.jsx("input",{type:"number",min:"1",step:"1",inputMode:"numeric","aria-label":s.key,value:l,disabled:n,placeholder:"default",onChange:d=>i(d.target.value),className:`w-full text-right tabular-nums sm:col-start-2 sm:w-28 sm:justify-self-end ${S}`}),e.jsx("div",{className:k,children:e.jsx(g,{size:"sm",variant:"primary","aria-label":`Save ${s.key}`,isDisabled:n||!o,onPress:()=>t(m===""?null:p),children:"Save"})}),a?e.jsx("div",{className:f,children:e.jsx(j,{message:a})}):null]})}function F({field:s,onSave:t,saveError:a,disabled:n}){const r=s.value===!0?"on":s.value===!1?"off":"default";return e.jsxs("div",{className:b,children:[e.jsx(y,{field:s}),e.jsx("div",{className:"sm:col-start-2 sm:justify-self-start",children:e.jsx(U,{ariaLabel:s.key,value:r,onChange:l=>t(l==="default"?null:l==="on"),options:[{value:"default",label:"Default"},{value:"on",label:"On"},{value:"off",label:"Off"}],disabled:n})}),a?e.jsx("div",{className:f,children:e.jsx(j,{message:a})}):null]})}function O({field:s,onSave:t,saveError:a,disabled:n}){return s.type==="url"?e.jsx(G,{field:s,onSave:t,saveError:a,disabled:n}):s.type==="int"?e.jsx(A,{field:s,onSave:t,saveError:a,disabled:n}):s.type==="bool"?e.jsx(F,{field:s,onSave:t,saveError:a,disabled:n}):e.jsx(I,{field:s,onSave:t,saveError:a,disabled:n})}function V(){const s=L(),t=C(),[a,n]=$(),[r,l]=x.useState({}),i=s.data,m=!i||t.isPending,p=new Map(((i==null?void 0:i.fields)??[]).map(o=>[o.key,o])),c=(o,d)=>{l(h=>{const{[o.key]:v,...u}=h;return u}),t.mutate(M(o.key,d),{onSuccess:()=>n(`${o.key} saved`),onError:h=>l(v=>({...v,[o.key]:w(h)}))})};return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(P,{title:"Tools & Guardrails",description:"Configure the built-in tool and guardrail service endpoints without a restart. Changes apply immediately and persist. URLs are validated for shape (http/https) and can be tested for reachability before saving; the network-safety gates for these services live on the Settings page."}),e.jsx(E,{error:s.error}),s.isLoading?e.jsx(R,{}):null,B.map(o=>{const d=o.order.map(u=>p.get(u)).filter(u=>u!==void 0),h=((i==null?void 0:i.fields)??[]).filter(u=>u.service===o.key&&!o.order.includes(u.key)),v=[...d,...h];return v.length===0?null:e.jsxs("section",{className:"flex flex-col gap-2",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:o.label}),e.jsx("p",{className:"text-sm text-[var(--otari-muted)]",children:o.blurb}),e.jsx(N,{children:e.jsx(N.Content,{className:"flex flex-col divide-y divide-[var(--otari-line)] px-5 py-1",children:v.map(u=>e.jsx(O,{field:u,onSave:T=>c(u,T),saveError:r[u.key],disabled:m},u.key))})})]},o.key)}),e.jsx(z,{message:a})]})}export{V as ToolsGuardrailsPage}; diff --git a/src/gateway/static/dashboard/assets/UsagePage-BYqtE3tC.js b/src/gateway/static/dashboard/assets/UsagePage-BYqtE3tC.js new file mode 100644 index 00000000..26735636 --- /dev/null +++ b/src/gateway/static/dashboard/assets/UsagePage-BYqtE3tC.js @@ -0,0 +1 @@ +import{j as e}from"./tanstack-query-1t81HyiD.js";import{i as Ae,r as i}from"./react-dgEcD0HR.js";import{u as Ue,a as Re,k as te,e as Oe,d as W,W as T,aj as ke,P as $e,E as ye,ak as je,R as Ke,i as se,w as Be,X as q,Z as A,Y as V,$ as Ie,al as B,f as ze,g as He,h as Ye,b as Ze,c as xe}from"./index-Br3lxdY5.js";import{A as Ge,F as We}from"./FilterChips-BbDynOdb.js";import{u as Qe,r as Ve,B as Je}from"./tableSelection-9hV37uhu.js";import{S as ae,B as Xe}from"./charts-DIsd9m--.js";import{C as et}from"./ConfirmDialog-KQrh5qGO.js";import{D as we}from"./DataTable-Bmn_eYSV.js";import{T as tt,S as st}from"./TablePagination-BR55oZ8k.js";import{B as I,S as pe}from"./heroui-BI50yK5B.js";import"./recharts-C4kRDmvS.js";function Q(t){return t.toLocaleString()}function at(t){return t===null?"—":t<1e3?`${Math.round(t)} ms`:`${(t/1e3).toFixed(2)} s`}const fe=ze(je,ke),ne=15,_e="__other__",ve="__unknown__";function Se({title:t,rows:r,totalCost:h,emptyLabel:n,onDrill:v,loading:m}){const[c,x]=i.useState(!1),j=c?r:r.slice(0,ne),U=r.length-j.length,N=o=>o.is_other?_e:o.key??ve,R=[{id:"name",header:t.replace("Spend by ",""),isRowHeader:!0,cell:o=>{const d=h>0?o.cost/h:0;return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("span",{className:"truncate text-[var(--otari-ink)]",children:o.is_other?`Other (${o.requests.toLocaleString()} req)`:o.key===null?"(unknown)":o.key}),e.jsx("span",{className:"h-1 w-full overflow-hidden rounded-full bg-[var(--otari-line)]",children:e.jsx("span",{className:"block h-full rounded-full bg-[var(--otari-brand)]",style:{width:`${Math.min(100,d*100)}%`}})})]})}},{id:"requests",header:"Requests",align:"end",cell:o=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:Q(o.requests)})},{id:"spend",header:"Spend",align:"end",cell:o=>e.jsx("span",{className:"text-[var(--otari-ink)]",children:V(o.cost)})}];return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:t}),e.jsx(we,{ariaLabel:t,columns:R,rows:j,getRowKey:N,isLoading:m,emptyContent:n,onRowAction:o=>{o!==_e&&o!==ve&&v(o)}}),!m&&U>0?e.jsxs(I,{size:"sm",variant:"ghost",onPress:()=>x(!0),children:["Show all ",r.length]}):null,!m&&c&&r.length>ne?e.jsxs(I,{size:"sm",variant:"ghost",onPress:()=>x(!1),children:["Show top ",ne]}):null]})}const nt=[{key:"cost",label:"Cost"},{key:"tokens",label:"Tokens"},{key:"requests",label:"Requests"}];function ot(t,r){return r==="cost"?t.cost:r==="tokens"?t.tokens:t.requests}function be(t,r){return r==="cost"?V(t):r==="tokens"?B(t):Q(t)}function rt(t,r){const h=new Date(t);return Number.isNaN(h.getTime())?t:r==="hour"?h.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit",timeZone:"UTC"}):h.toLocaleDateString(void 0,{month:"short",day:"numeric",timeZone:"UTC"})}function lt(t,r,h){const n=t.map(m=>({label:rt(m.bucket_start,h),value:ot(m,r)})),v=n.length?Math.max(...n.map(m=>m.value)):0;return{points:n,peak:v,count:t.length}}const it=50;function ct(t){const r=new Date(t).getTime();if(Number.isNaN(r))return t;const h=Math.max(0,Math.round((Date.now()-r)/1e3));if(h<60)return`${h}s ago`;const n=Math.round(h/60);if(n<60)return`${n}m ago`;const v=Math.round(n/60);return v<24?`${v}h ago`:`${Math.round(v/24)}d ago`}function dt({status:t}){return e.jsx("span",{className:`inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium ${t==="error"?"border-red-200 bg-red-50 text-red-700":"border-[var(--otari-line)] bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]"}`,children:t})}const ut=[{id:"time",header:"Time",isRowHeader:!0,cell:t=>e.jsx("span",{className:"text-[var(--otari-muted)]",title:new Date(t.timestamp).toLocaleString(),children:ct(t.timestamp)})},{id:"model",header:"Model",cell:t=>e.jsx("span",{className:"text-[var(--otari-ink)]",children:t.model})},{id:"user",header:"User",cell:t=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:t.user_id??"—"})},{id:"tokens",header:"Tokens",align:"end",cell:t=>t.total_tokens===null?"—":t.total_tokens.toLocaleString()},{id:"cost",header:"Cost",align:"end",cell:t=>t.cost===null?"—":V(t.cost)},{id:"status",header:"Status",cell:t=>e.jsx(dt,{status:t.status})}],mt=t=>t.id;function ht({filters:t,anyFilter:r}){var K,H;const[h,n]=i.useState(0),[v,m]=i.useState(it),c=Qe(),x=He(),j=Ye(),[U,N]=i.useState(!1),[R,o]=i.useState(!1),d=JSON.stringify(t),L=i.useRef(d);i.useEffect(()=>{L.current!==d&&(L.current=d,n(0),c.clear())},[d,c]);const p=Ze(t,h,v),C=xe(t),g=p.data??[],P=C.isSuccess&&!C.isPlaceholderData?((K=C.data)==null?void 0:K.total)??0:null,O=g.filter(_=>!_.counts_toward_budget).map(_=>_.id),f=g.filter(_=>_.counts_toward_budget).map(_=>_.id),S=Ve(c.selectedKeys,O),b=S.length,y=c.allMatching||b>0,D=i.useMemo(()=>({...t,counts_toward_budget:!1}),[t]),k=xe(D,y),E=k.isSuccess?((H=k.data)==null?void 0:H.total)??null:null,$=O.length>0&&b===O.length&&E!=null&&E>b,F=c.allMatching?E??b:b,l=()=>c.allMatching?{by_filter:!0,model:t.model,user_id:t.user_id,api_key_id:t.api_key_id,start_date:t.start_date,end_date:t.end_date,priced:t.priced}:{ids:S},a=()=>x.mutate(l(),{onSuccess:()=>{N(!1),c.clear()}}),u=_=>j.mutate({...l(),..._},{onSuccess:()=>{o(!1),c.clear()}});return e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Individual requests"}),e.jsx(ye,{error:p.error??C.error}),y?e.jsxs(Je,{selectedCount:F,allMatching:c.allMatching,matchingTotal:E,canSelectAllMatching:$,onSelectAllMatching:c.enableAllMatching,onClear:c.clear,children:[e.jsx(I,{size:"sm",variant:"primary",onPress:()=>o(!0),children:"Set price"}),e.jsx(I,{size:"sm",variant:"danger",onPress:()=>N(!0),children:"Delete"})]}):null,e.jsx(we,{ariaLabel:"Individual requests",columns:ut,rows:g,getRowKey:mt,isLoading:p.isLoading,emptyContent:r?"No requests match these filters.":"No requests recorded yet.",selectionMode:"multiple",selectedKeys:c.selectedKeys,onSelectionChange:c.onSelectionChange,disabledKeys:f}),e.jsx(tt,{page:h,pageSize:v,total:P,rowsOnPage:g.length,onPageChange:n,onPageSizeChange:_=>{m(_),n(0)},isFetching:p.isFetching,hasNextFallback:g.length===v}),e.jsx(et,{isOpen:U,onOpenChange:N,heading:"Delete usage rows",body:`Delete ${F.toLocaleString()} imported ${F===1?"row":"rows"}? Only imported rows are removed, and this cannot be undone.`,confirmLabel:"Delete",isPending:x.isPending,error:x.error,onConfirm:a}),e.jsx(st,{isOpen:R,onOpenChange:o,targetCount:F,isPending:j.isPending,error:j.error,onSubmit:u})]})}function wt(){var ue,me,he,ge;const t=Ae(),r=Ue(),h=Re(),[n,v]=i.useState(fe),[m,c]=i.useState(()=>te(fe.seconds??0)),[x,j]=i.useState(!1),[U,N]=i.useState(),[R,o]=i.useState(),[d,L]=i.useState(""),[p,C]=i.useState(""),[g,z]=i.useState(""),[P,O]=i.useState("cost"),f=x?U:m,S=x?R:void 0,b=x?f?Oe(f,S):"day":n.bucket,y=i.useMemo(()=>({start_date:f,end_date:S,model:d.trim()||void 0,user_id:p||void 0,api_key_id:g||void 0}),[f,S,d,p,g]),D=i.useMemo(()=>{if(x){if(!f||!S)return null;const s=new Date(S).getTime()-new Date(f).getTime();return s>0?{...y,start_date:new Date(new Date(f).getTime()-s).toISOString(),end_date:f}:null}return!m||n.seconds===null?null:{...y,start_date:new Date(new Date(m).getTime()-n.seconds*1e3).toISOString(),end_date:m}},[x,f,S,y,n.seconds,m]),k=W(y,b),E=W(D??y,b,D!==null),oe=i.useMemo(()=>({start_date:m,model:d.trim()||void 0,user_id:p||void 0,api_key_id:g||void 0}),[m,d,p,g]),$=W(oe,n.bucket),F=(((ue=$.data)==null?void 0:ue.series)??[]).map(s=>({bucketStart:s.bucket_start,requests:s.requests})),l=k.data,a=l==null?void 0:l.totals,u=D!==null?(me=E.data)==null?void 0:me.totals:void 0,K=a?T(a.cost,u==null?void 0:u.cost):null,H=i.useMemo(()=>({...y,model:void 0}),[y]),_=W(H,b),J=((ge=(he=_.data)==null?void 0:he.by_model)==null?void 0:ge.filter(s=>!s.is_other&&s.key!==null).map(s=>s.key))??[],re=(r.data??[]).map(s=>({value:s.user_id,label:s.alias?`${s.alias} (${s.user_id})`:s.user_id})),le=(h.data??[]).map(s=>({value:s.id,label:s.key_name??`${s.id.slice(0,8)}…`})),Ne=(d&&!J.includes(d)?[d,...J]:J).map(s=>({value:s,label:s})),Ce=x||n.key!==ke,Y=!!(d.trim()||p||g||Ce),ie=(s,w)=>{var Z;return((Z=s.find(G=>G.value===w))==null?void 0:Z.label)??w},Pe=()=>{L(""),C(""),z("")},Ee=[...p?[{key:"user",label:"User",value:ie(re,p),onClear:()=>C("")}]:[],...d.trim()?[{key:"model",label:"Model",value:d.trim(),onClear:()=>L("")}]:[],...g?[{key:"key",label:"API key",value:ie(le,g),onClear:()=>z("")}]:[]],qe=!!(l&&a&&a.request_count===0&&!Y),Le=(l==null?void 0:l.start_date)??f,De=(l==null?void 0:l.end_date)??S,ce=s=>{j(!1),v(s),c(te(s.seconds??0)),N(void 0),o(void 0)},Fe=(s,w)=>{j(!0),N(s),o(w)},Me=()=>{x||c(te(n.seconds??0)),k.refetch(),$.refetch(),_.refetch(),D!==null&&E.refetch()},de=s=>{const w=new URLSearchParams;f&&w.set("start_date",f),S&&w.set("end_date",S);for(const[Z,G]of Object.entries(s))G&&w.set(Z,G);t(`/activity?${w.toString()}`)},Te=a&&a.request_count>0?a.error_count/a.request_count:0,M=(l==null?void 0:l.series)??[],X=M.length>1,ee=lt(M,P,b);return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx($e,{title:"Usage & analytics",description:"Aggregate spend, tokens, and request volume over time. Click a model or user to drill into the request log."}),e.jsx(ye,{error:k.error}),e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx(Ge,{presets:je,extentKey:n.key,onPreset:ce,onSelectRange:Fe,onSelectFull:()=>ce(n),series:F,bucket:n.bucket,windowStart:Le,windowEnd:De,loading:$.isLoading,ariaLabel:"Usage request volume over the selected window",action:e.jsx(Ke,{onRefresh:Me,isFetching:k.isFetching,updatedAt:k.dataUpdatedAt})}),e.jsxs(We,{chips:Ee,onClearAll:Pe,children:[e.jsx(se,{label:"User",value:p,onChange:C,options:re,placeholder:"All users"}),e.jsx(se,{label:"Model",value:d,onChange:L,options:Ne,placeholder:"All models"}),e.jsx(se,{label:"API key",value:g,onChange:z,options:le,placeholder:"All keys"})]})]}),qe?e.jsx(Be,{title:"No usage yet",description:"Once the gateway serves requests, spend and volume appear here."}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 sm:grid-cols-3 xl:grid-cols-4",children:[e.jsx(q,{label:"Tracked cost",value:a?V(a.cost):"—",hint:a?e.jsxs("span",{className:"text-[var(--otari-muted)]",children:[e.jsx(A,{fraction:K}),a.unpriced_requests?`${K!==null?" · ":""}${Q(a.unpriced_requests)} unpriced`:null]}):null,chart:X?e.jsx(ae,{values:M.map(s=>s.cost),ariaLabel:"Spend trend over the selected window"}):void 0}),e.jsx(q,{label:"Requests",value:a?Q(a.request_count):"—",hint:a?e.jsxs("span",{className:"text-[var(--otari-muted)]",children:[Ie(Te)," errors",u?e.jsxs(e.Fragment,{children:[" · ",e.jsx(A,{fraction:T(a.request_count,u.request_count)})]}):null]}):null,chart:X?e.jsx(ae,{values:M.map(s=>s.requests),ariaLabel:"Request volume trend over the selected window"}):void 0}),e.jsx(q,{label:"Tokens",value:a?B(a.total_tokens):"—",hint:a?e.jsx(A,{fraction:T(a.total_tokens,u==null?void 0:u.total_tokens)}):null,chart:X?e.jsx(ae,{values:M.map(s=>s.tokens),ariaLabel:"Token usage trend over the selected window"}):void 0}),e.jsx(q,{label:"Cache read",value:a?B(a.cache_read_tokens):"—",hint:a?e.jsx(A,{fraction:T(a.cache_read_tokens,u==null?void 0:u.cache_read_tokens)}):null}),e.jsx(q,{label:"Cache write",value:a?B(a.cache_write_tokens):"—",hint:a?e.jsx(A,{fraction:T(a.cache_write_tokens,u==null?void 0:u.cache_write_tokens)}):null}),e.jsx(q,{label:"1h cache write",value:a?B(a.cache_write_1h_tokens??0):"—",hint:a?e.jsx(A,{fraction:T(a.cache_write_1h_tokens??0,(u==null?void 0:u.cache_write_1h_tokens)??0)}):null}),e.jsx(q,{label:"Avg latency",value:a?at(a.avg_latency_ms):"—"})]}),e.jsxs("div",{className:"grid gap-6 lg:grid-cols-2",children:[e.jsx(Se,{title:"Spend by model",rows:(l==null?void 0:l.by_model)??[],totalCost:(a==null?void 0:a.cost)??0,emptyLabel:Y?"No usage matches these filters.":"No usage recorded yet.",onDrill:s=>de({model:s,user_id:p||void 0,api_key_id:g||void 0}),loading:k.isLoading}),e.jsx(Se,{title:"Spend by user",rows:(l==null?void 0:l.by_user)??[],totalCost:(a==null?void 0:a.cost)??0,emptyLabel:Y?"No usage matches these filters.":"No usage recorded yet.",onDrill:s=>de({user_id:s,model:d.trim()||void 0,api_key_id:g||void 0}),loading:k.isLoading})]}),e.jsx(ht,{filters:y,anyFilter:Y}),e.jsxs("div",{className:"flex flex-col gap-3 rounded-xl border border-[var(--otari-line)] bg-[var(--otari-surface)] p-4",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Over time"}),e.jsxs("div",{className:"inline-flex gap-1.5",children:[nt.map(s=>e.jsx(I,{size:"sm",variant:P===s.key?"primary":"outline",onPress:()=>O(s.key),children:s.label},s.key)),k.isFetching?e.jsx(pe,{size:"sm"}):null]})]}),k.isLoading?e.jsx("div",{className:"flex h-48 items-center justify-center",children:e.jsx(pe,{size:"sm"})}):M.length===0?e.jsx("div",{className:"flex h-48 items-center justify-center text-sm text-[var(--otari-muted)]",children:"No data in this range."}):e.jsxs("figure",{className:"flex flex-col gap-2",children:[e.jsx(Xe,{data:ee.points,formatValue:s=>be(s,P),ariaLabel:`${P} per ${b}`}),e.jsxs("figcaption",{className:"text-xs text-[var(--otari-muted)]",children:[be(ee.peak,P)," peak · ",ee.count," ",b==="hour"?"hours":"days"," (times in UTC)"]})]})]})]})]})}export{wt as UsagePage}; diff --git a/src/gateway/static/dashboard/assets/UsagePage-Ce_jAzFl.js b/src/gateway/static/dashboard/assets/UsagePage-Ce_jAzFl.js deleted file mode 100644 index ee68cbdc..00000000 --- a/src/gateway/static/dashboard/assets/UsagePage-Ce_jAzFl.js +++ /dev/null @@ -1 +0,0 @@ -import{j as e}from"./tanstack-query-1t81HyiD.js";import{i as Ue,r as i}from"./react-dgEcD0HR.js";import{u as Ae,a as Re,d as Y,O as T,P as Oe,E as ye,R as Ke,g as te,q as $e,S as q,T as U,Q as W,V as Be,ad as B,e as Ie,f as ze,b as He,c as xe}from"./index-DV8qvdUj.js";import{i as se,b as Ge,U as ke,A as Qe,d as je,F as Ve,f as Ye}from"./FilterChips-DZjxf-ot.js";import{u as Ze,r as We,B as Je}from"./tableSelection-9hV37uhu.js";import{S as ae,B as Xe}from"./charts-DIsd9m--.js";import{C as et}from"./ConfirmDialog-KvA7b1-a.js";import{D as we}from"./DataTable-Bmn_eYSV.js";import{T as tt,S as st}from"./TablePagination-CXhesdmH.js";import{B as I,S as pe}from"./heroui-BI50yK5B.js";import"./recharts-C4kRDmvS.js";function Z(t){return t.toLocaleString()}function at(t){return t===null?"—":t<1e3?`${Math.round(t)} ms`:`${(t/1e3).toFixed(2)} s`}const fe=Ye(je,ke),ne=15,_e="__other__",ve="__unknown__";function Se({title:t,rows:r,totalCost:h,emptyLabel:n,onDrill:v,loading:m}){const[c,x]=i.useState(!1),j=c?r:r.slice(0,ne),A=r.length-j.length,N=o=>o.is_other?_e:o.key??ve,R=[{id:"name",header:t.replace("Spend by ",""),isRowHeader:!0,cell:o=>{const d=h>0?o.cost/h:0;return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("span",{className:"truncate text-[var(--otari-ink)]",children:o.is_other?`Other (${o.requests.toLocaleString()} req)`:o.key===null?"(unknown)":o.key}),e.jsx("span",{className:"h-1 w-full overflow-hidden rounded-full bg-[var(--otari-line)]",children:e.jsx("span",{className:"block h-full rounded-full bg-[var(--otari-brand)]",style:{width:`${Math.min(100,d*100)}%`}})})]})}},{id:"requests",header:"Requests",align:"end",cell:o=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:Z(o.requests)})},{id:"spend",header:"Spend",align:"end",cell:o=>e.jsx("span",{className:"text-[var(--otari-ink)]",children:W(o.cost)})}];return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:t}),e.jsx(we,{ariaLabel:t,columns:R,rows:j,getRowKey:N,isLoading:m,emptyContent:n,onRowAction:o=>{o!==_e&&o!==ve&&v(o)}}),!m&&A>0?e.jsxs(I,{size:"sm",variant:"ghost",onPress:()=>x(!0),children:["Show all ",r.length]}):null,!m&&c&&r.length>ne?e.jsxs(I,{size:"sm",variant:"ghost",onPress:()=>x(!1),children:["Show top ",ne]}):null]})}const nt=[{key:"cost",label:"Cost"},{key:"tokens",label:"Tokens"},{key:"requests",label:"Requests"}];function ot(t,r){return r==="cost"?t.cost:r==="tokens"?t.tokens:t.requests}function be(t,r){return r==="cost"?W(t):r==="tokens"?B(t):Z(t)}function rt(t,r){const h=new Date(t);return Number.isNaN(h.getTime())?t:r==="hour"?h.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit",timeZone:"UTC"}):h.toLocaleDateString(void 0,{month:"short",day:"numeric",timeZone:"UTC"})}function lt(t,r,h){const n=t.map(m=>({label:rt(m.bucket_start,h),value:ot(m,r)})),v=n.length?Math.max(...n.map(m=>m.value)):0;return{points:n,peak:v,count:t.length}}const it=50;function ct(t){const r=new Date(t).getTime();if(Number.isNaN(r))return t;const h=Math.max(0,Math.round((Date.now()-r)/1e3));if(h<60)return`${h}s ago`;const n=Math.round(h/60);if(n<60)return`${n}m ago`;const v=Math.round(n/60);return v<24?`${v}h ago`:`${Math.round(v/24)}d ago`}function dt({status:t}){return e.jsx("span",{className:`inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium ${t==="error"?"border-red-200 bg-red-50 text-red-700":"border-[var(--otari-line)] bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]"}`,children:t})}const ut=[{id:"time",header:"Time",isRowHeader:!0,cell:t=>e.jsx("span",{className:"text-[var(--otari-muted)]",title:new Date(t.timestamp).toLocaleString(),children:ct(t.timestamp)})},{id:"model",header:"Model",cell:t=>e.jsx("span",{className:"text-[var(--otari-ink)]",children:t.model})},{id:"user",header:"User",cell:t=>e.jsx("span",{className:"text-[var(--otari-muted)]",children:t.user_id??"—"})},{id:"tokens",header:"Tokens",align:"end",cell:t=>t.total_tokens===null?"—":t.total_tokens.toLocaleString()},{id:"cost",header:"Cost",align:"end",cell:t=>t.cost===null?"—":W(t.cost)},{id:"status",header:"Status",cell:t=>e.jsx(dt,{status:t.status})}],mt=t=>t.id;function ht({filters:t,anyFilter:r}){var $,H;const[h,n]=i.useState(0),[v,m]=i.useState(it),c=Ze(),x=Ie(),j=ze(),[A,N]=i.useState(!1),[R,o]=i.useState(!1),d=JSON.stringify(t),L=i.useRef(d);i.useEffect(()=>{L.current!==d&&(L.current=d,n(0),c.clear())},[d,c]);const p=He(t,h,v),C=xe(t),g=p.data??[],P=C.isSuccess&&!C.isPlaceholderData?(($=C.data)==null?void 0:$.total)??0:null,O=g.filter(_=>!_.counts_toward_budget).map(_=>_.id),f=g.filter(_=>_.counts_toward_budget).map(_=>_.id),S=We(c.selectedKeys,O),b=S.length,k=c.allMatching||b>0,D=i.useMemo(()=>({...t,counts_toward_budget:!1}),[t]),y=xe(D,k),E=y.isSuccess?((H=y.data)==null?void 0:H.total)??null:null,K=O.length>0&&b===O.length&&E!=null&&E>b,F=c.allMatching?E??b:b,l=()=>c.allMatching?{by_filter:!0,model:t.model,user_id:t.user_id,api_key_id:t.api_key_id,start_date:t.start_date,end_date:t.end_date,priced:t.priced}:{ids:S},a=()=>x.mutate(l(),{onSuccess:()=>{N(!1),c.clear()}}),u=_=>j.mutate({...l(),..._},{onSuccess:()=>{o(!1),c.clear()}});return e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Individual requests"}),e.jsx(ye,{error:p.error??C.error}),k?e.jsxs(Je,{selectedCount:F,allMatching:c.allMatching,matchingTotal:E,canSelectAllMatching:K,onSelectAllMatching:c.enableAllMatching,onClear:c.clear,children:[e.jsx(I,{size:"sm",variant:"primary",onPress:()=>o(!0),children:"Set price"}),e.jsx(I,{size:"sm",variant:"danger",onPress:()=>N(!0),children:"Delete"})]}):null,e.jsx(we,{ariaLabel:"Individual requests",columns:ut,rows:g,getRowKey:mt,isLoading:p.isLoading,emptyContent:r?"No requests match these filters.":"No requests recorded yet.",selectionMode:"multiple",selectedKeys:c.selectedKeys,onSelectionChange:c.onSelectionChange,disabledKeys:f}),e.jsx(tt,{page:h,pageSize:v,total:P,rowsOnPage:g.length,onPageChange:n,onPageSizeChange:_=>{m(_),n(0)},isFetching:p.isFetching,hasNextFallback:g.length===v}),e.jsx(et,{isOpen:A,onOpenChange:N,heading:"Delete usage rows",body:`Delete ${F.toLocaleString()} imported ${F===1?"row":"rows"}? Only imported rows are removed, and this cannot be undone.`,confirmLabel:"Delete",isPending:x.isPending,error:x.error,onConfirm:a}),e.jsx(st,{isOpen:R,onOpenChange:o,targetCount:F,isPending:j.isPending,error:j.error,onSubmit:u})]})}function wt(){var ue,me,he,ge;const t=Ue(),r=Ae(),h=Re(),[n,v]=i.useState(fe),[m,c]=i.useState(()=>se(fe.seconds??0)),[x,j]=i.useState(!1),[A,N]=i.useState(),[R,o]=i.useState(),[d,L]=i.useState(""),[p,C]=i.useState(""),[g,z]=i.useState(""),[P,O]=i.useState("cost"),f=x?A:m,S=x?R:void 0,b=x?f?Ge(f,S):"day":n.bucket,k=i.useMemo(()=>({start_date:f,end_date:S,model:d.trim()||void 0,user_id:p||void 0,api_key_id:g||void 0}),[f,S,d,p,g]),D=i.useMemo(()=>{if(x){if(!f||!S)return null;const s=new Date(S).getTime()-new Date(f).getTime();return s>0?{...k,start_date:new Date(new Date(f).getTime()-s).toISOString(),end_date:f}:null}return!m||n.seconds===null?null:{...k,start_date:new Date(new Date(m).getTime()-n.seconds*1e3).toISOString(),end_date:m}},[x,f,S,k,n.seconds,m]),y=Y(k,b),E=Y(D??k,b,D!==null),oe=i.useMemo(()=>({start_date:m,model:d.trim()||void 0,user_id:p||void 0,api_key_id:g||void 0}),[m,d,p,g]),K=Y(oe,n.bucket),F=(((ue=K.data)==null?void 0:ue.series)??[]).map(s=>({bucketStart:s.bucket_start,requests:s.requests})),l=y.data,a=l==null?void 0:l.totals,u=D!==null?(me=E.data)==null?void 0:me.totals:void 0,$=a?T(a.cost,u==null?void 0:u.cost):null,H=i.useMemo(()=>({...k,model:void 0}),[k]),_=Y(H,b),J=((ge=(he=_.data)==null?void 0:he.by_model)==null?void 0:ge.filter(s=>!s.is_other&&s.key!==null).map(s=>s.key))??[],re=(r.data??[]).map(s=>({value:s.user_id,label:s.alias?`${s.alias} (${s.user_id})`:s.user_id})),le=(h.data??[]).map(s=>({value:s.id,label:s.key_name??`${s.id.slice(0,8)}…`})),Ne=(d&&!J.includes(d)?[d,...J]:J).map(s=>({value:s,label:s})),Ce=x||n.key!==ke,G=!!(d.trim()||p||g||Ce),ie=(s,w)=>{var Q;return((Q=s.find(V=>V.value===w))==null?void 0:Q.label)??w},Pe=()=>{L(""),C(""),z("")},Ee=[...p?[{key:"user",label:"User",value:ie(re,p),onClear:()=>C("")}]:[],...d.trim()?[{key:"model",label:"Model",value:d.trim(),onClear:()=>L("")}]:[],...g?[{key:"key",label:"API key",value:ie(le,g),onClear:()=>z("")}]:[]],qe=!!(l&&a&&a.request_count===0&&!G),Le=(l==null?void 0:l.start_date)??f,De=(l==null?void 0:l.end_date)??S,ce=s=>{j(!1),v(s),c(se(s.seconds??0)),N(void 0),o(void 0)},Fe=(s,w)=>{j(!0),N(s),o(w)},Me=()=>{x||c(se(n.seconds??0)),y.refetch(),K.refetch(),_.refetch(),D!==null&&E.refetch()},de=s=>{const w=new URLSearchParams;f&&w.set("start_date",f),S&&w.set("end_date",S);for(const[Q,V]of Object.entries(s))V&&w.set(Q,V);t(`/activity?${w.toString()}`)},Te=a&&a.request_count>0?a.error_count/a.request_count:0,M=(l==null?void 0:l.series)??[],X=M.length>1,ee=lt(M,P,b);return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(Oe,{title:"Usage & analytics",description:"Aggregate spend, tokens, and request volume over time. Click a model or user to drill into the request log."}),e.jsx(ye,{error:y.error}),e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx(Qe,{presets:je,extentKey:n.key,onPreset:ce,onSelectRange:Fe,onSelectFull:()=>ce(n),series:F,bucket:n.bucket,windowStart:Le,windowEnd:De,loading:K.isLoading,ariaLabel:"Usage request volume over the selected window",action:e.jsx(Ke,{onRefresh:Me,isFetching:y.isFetching,updatedAt:y.dataUpdatedAt})}),e.jsxs(Ve,{chips:Ee,onClearAll:Pe,children:[e.jsx(te,{label:"User",value:p,onChange:C,options:re,placeholder:"All users"}),e.jsx(te,{label:"Model",value:d,onChange:L,options:Ne,placeholder:"All models"}),e.jsx(te,{label:"API key",value:g,onChange:z,options:le,placeholder:"All keys"})]})]}),qe?e.jsx($e,{title:"No usage yet",description:"Once the gateway serves requests, spend and volume appear here."}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 sm:grid-cols-3 xl:grid-cols-4",children:[e.jsx(q,{label:"Tracked cost",value:a?W(a.cost):"—",hint:a?e.jsxs("span",{className:"text-[var(--otari-muted)]",children:[e.jsx(U,{fraction:$}),a.unpriced_requests?`${$!==null?" · ":""}${Z(a.unpriced_requests)} unpriced`:null]}):null,chart:X?e.jsx(ae,{values:M.map(s=>s.cost),ariaLabel:"Spend trend over the selected window"}):void 0}),e.jsx(q,{label:"Requests",value:a?Z(a.request_count):"—",hint:a?e.jsxs("span",{className:"text-[var(--otari-muted)]",children:[Be(Te)," errors",u?e.jsxs(e.Fragment,{children:[" · ",e.jsx(U,{fraction:T(a.request_count,u.request_count)})]}):null]}):null,chart:X?e.jsx(ae,{values:M.map(s=>s.requests),ariaLabel:"Request volume trend over the selected window"}):void 0}),e.jsx(q,{label:"Tokens",value:a?B(a.total_tokens):"—",hint:a?e.jsx(U,{fraction:T(a.total_tokens,u==null?void 0:u.total_tokens)}):null,chart:X?e.jsx(ae,{values:M.map(s=>s.tokens),ariaLabel:"Token usage trend over the selected window"}):void 0}),e.jsx(q,{label:"Cache read",value:a?B(a.cache_read_tokens):"—",hint:a?e.jsx(U,{fraction:T(a.cache_read_tokens,u==null?void 0:u.cache_read_tokens)}):null}),e.jsx(q,{label:"Cache write",value:a?B(a.cache_write_tokens):"—",hint:a?e.jsx(U,{fraction:T(a.cache_write_tokens,u==null?void 0:u.cache_write_tokens)}):null}),e.jsx(q,{label:"1h cache write",value:a?B(a.cache_write_1h_tokens??0):"—",hint:a?e.jsx(U,{fraction:T(a.cache_write_1h_tokens??0,(u==null?void 0:u.cache_write_1h_tokens)??0)}):null}),e.jsx(q,{label:"Avg latency",value:a?at(a.avg_latency_ms):"—"})]}),e.jsxs("div",{className:"grid gap-6 lg:grid-cols-2",children:[e.jsx(Se,{title:"Spend by model",rows:(l==null?void 0:l.by_model)??[],totalCost:(a==null?void 0:a.cost)??0,emptyLabel:G?"No usage matches these filters.":"No usage recorded yet.",onDrill:s=>de({model:s,user_id:p||void 0,api_key_id:g||void 0}),loading:y.isLoading}),e.jsx(Se,{title:"Spend by user",rows:(l==null?void 0:l.by_user)??[],totalCost:(a==null?void 0:a.cost)??0,emptyLabel:G?"No usage matches these filters.":"No usage recorded yet.",onDrill:s=>de({user_id:s,model:d.trim()||void 0,api_key_id:g||void 0}),loading:y.isLoading})]}),e.jsx(ht,{filters:k,anyFilter:G}),e.jsxs("div",{className:"flex flex-col gap-3 rounded-xl border border-[var(--otari-line)] bg-[var(--otari-surface)] p-4",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Over time"}),e.jsxs("div",{className:"inline-flex gap-1.5",children:[nt.map(s=>e.jsx(I,{size:"sm",variant:P===s.key?"primary":"outline",onPress:()=>O(s.key),children:s.label},s.key)),y.isFetching?e.jsx(pe,{size:"sm"}):null]})]}),y.isLoading?e.jsx("div",{className:"flex h-48 items-center justify-center",children:e.jsx(pe,{size:"sm"})}):M.length===0?e.jsx("div",{className:"flex h-48 items-center justify-center text-sm text-[var(--otari-muted)]",children:"No data in this range."}):e.jsxs("figure",{className:"flex flex-col gap-2",children:[e.jsx(Xe,{data:ee.points,formatValue:s=>be(s,P),ariaLabel:`${P} per ${b}`}),e.jsxs("figcaption",{className:"text-xs text-[var(--otari-muted)]",children:[be(ee.peak,P)," peak · ",ee.count," ",b==="hour"?"hours":"days"," (times in UTC)"]})]})]})]})]})}export{wt as UsagePage}; diff --git a/src/gateway/static/dashboard/assets/UsersPage-5kctrw19.js b/src/gateway/static/dashboard/assets/UsersPage-CZXIBvdD.js similarity index 97% rename from src/gateway/static/dashboard/assets/UsersPage-5kctrw19.js rename to src/gateway/static/dashboard/assets/UsersPage-CZXIBvdD.js index 68dd0bbb..cae35818 100644 --- a/src/gateway/static/dashboard/assets/UsersPage-5kctrw19.js +++ b/src/gateway/static/dashboard/assets/UsersPage-CZXIBvdD.js @@ -1 +1 @@ -import{j as e}from"./tanstack-query-1t81HyiD.js";import{r}from"./react-dgEcD0HR.js";import{u as Q,l as M,p as K,ae as X,P as Y,E as A,q as Z,af as ee,F as se}from"./index-DV8qvdUj.js";import{u as te,r as ae,B as ne}from"./tableSelection-9hV37uhu.js";import{C as re}from"./ConfirmDialog-KvA7b1-a.js";import{D as le}from"./DataTable-Bmn_eYSV.js";import{F as B}from"./Field-Dwp6u3t8.js";import{a as ie,M as R}from"./ModelScopeControl-u-XlXgXz.js";import{f as I,B as d,d as S,A as f}from"./heroui-BI50yK5B.js";const de=new Intl.NumberFormat(void 0,{style:"currency",currency:"USD",maximumFractionDigits:4});function U(t){return de.format(t)}const oe=t=>t.user_id,D=t=>t.startsWith("apikey-");function H(t){return t.split("-")[0]}function E(t){return t.name??H(t.budget_id)}function $({value:t,onChange:l,budgets:n}){return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("label",{htmlFor:"user-budget",className:"text-sm font-medium text-[var(--otari-ink)]",children:"Budget"}),e.jsxs("select",{id:"user-budget",value:t??"",onChange:a=>l(a.target.value||null),className:"w-full rounded-lg border border-[var(--otari-line)] bg-[var(--otari-bg)] px-3 py-2 text-sm text-[var(--otari-ink)]",children:[e.jsx("option",{value:"",children:"No budget (unlimited)"}),n.map(a=>e.jsxs("option",{value:a.budget_id,children:[E(a),a.max_budget===null?" · no limit":` · ${U(a.max_budget)}`]},a.budget_id))]}),e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"The spending limit this user is held to. Manage budgets on the Budgets page."})]})}function ce({onClose:t}){const l=ee(),n=M(),[a,i]=r.useState(""),[o,u]=r.useState(""),[c,g]=r.useState(null),[p,m]=r.useState(null),[b,j]=r.useState(!0),x=()=>{if(l.isPending||!b||a.trim()==="")return;const h={user_id:a.trim(),alias:o.trim()||null,budget_id:c,allowed_models:p};l.mutate(h,{onSuccess:t})};return e.jsx(S,{children:e.jsxs(S.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsx("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Create user"}),e.jsx(A,{error:l.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(B,{label:"User ID",value:a,onChange:i,placeholder:"alice@example.com",isRequired:!0,autoFocus:!0,description:"The identifier callers send as the `user` field; spend and budgets track against it."}),e.jsx(B,{label:"Alias (optional)",value:o,onChange:u,placeholder:"Alice"})]}),e.jsx($,{value:c,onChange:g,budgets:n.data??[]}),e.jsx(R,{title:"Model access (default for this user's keys)",description:"The models this user's keys may list and call by default. A key can narrow this, but never exceed it.",initial:null,onChange:(h,C)=>{m(h),j(C)}}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(d,{variant:"primary",isDisabled:l.isPending||!b||a.trim()==="",onPress:x,children:l.isPending?"Creating…":"Create user"}),e.jsx(d,{variant:"ghost",onPress:t,children:"Cancel"})]})]})})}function ue({user:t,onClose:l}){const n=K(),a=M(),[i,o]=r.useState(t.alias??""),[u,c]=r.useState(t.budget_id),[g,p]=r.useState(t.allowed_models),[m,b]=r.useState(!0),j=()=>{if(n.isPending||!m)return;const x={alias:i.trim()||null,budget_id:u,allowed_models:g};n.mutate({id:t.user_id,body:x},{onSuccess:l})};return e.jsx(S,{children:e.jsxs(S.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsxs("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:["Edit ",e.jsx("code",{children:t.user_id})]}),e.jsx(A,{error:n.error}),e.jsx(B,{label:"Alias",value:i,onChange:o,placeholder:"Alice"}),e.jsx($,{value:u,onChange:c,budgets:a.data??[]}),e.jsx(R,{title:"Model access (default for this user's keys)",description:"The models this user's keys may list and call by default. A key can narrow this, but never exceed it.",initial:t.allowed_models,onChange:(x,h)=>{p(x),b(h)}}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(d,{variant:"primary",isDisabled:n.isPending||!m,onPress:j,children:n.isPending?"Saving…":"Save changes"}),e.jsx(d,{variant:"ghost",onPress:l,children:"Cancel"})]})]})})}function ge({trigger:t,message:l,confirmLabel:n,isPending:a,onConfirm:i}){const[o,u]=r.useState(!1);return o?e.jsxs("div",{className:"flex flex-col items-end gap-1.5 rounded-lg border border-amber-200 bg-amber-50 p-2 text-right",children:[e.jsx("span",{className:"max-w-xs text-xs text-amber-800",children:l}),e.jsxs("span",{className:"inline-flex gap-1",children:[e.jsx(d,{size:"sm",variant:"danger",isDisabled:a,onPress:i,children:n}),e.jsx(d,{size:"sm",variant:"ghost",isDisabled:a,onPress:()=>u(!1),children:"Cancel"})]})]}):e.jsx(d,{size:"sm",variant:"danger-soft",onPress:()=>u(!0),children:t})}function me({user:t}){return t.blocked?e.jsx(I,{size:"sm",color:"warning",children:"Blocked"}):e.jsx(I,{size:"sm",color:"accent",children:"Active"})}function xe({allowed:t}){const{text:l,tone:n}=ie(t),a=n==="danger"?"text-red-700 font-medium":n==="muted"?"text-[var(--otari-muted)]":"text-[var(--otari-brand-dark)] font-medium",i=t&&t.length>0?t.join(", "):void 0;return e.jsx("span",{className:`text-xs ${a}`,title:i,children:l})}function he({isOpen:t,onOpenChange:l,budgets:n,count:a,isPending:i,error:o,onAssign:u}){const[c,g]=r.useState("");return r.useEffect(()=>{t&&g("")},[t]),e.jsx(f,{isOpen:t,onOpenChange:l,children:t?e.jsx(f.Backdrop,{children:e.jsx(f.Container,{placement:"center",size:"md",children:e.jsxs(f.Dialog,{children:[e.jsx(f.Header,{children:e.jsx(f.Heading,{children:"Assign budget"})}),e.jsxs(f.Body,{className:"flex flex-col gap-4",children:[e.jsxs("p",{className:"text-sm text-[var(--otari-muted)]",children:["Assign a budget to ",a," selected ",a===1?"user":"users","."]}),e.jsx(se,{label:"Budget",value:c,onChange:g,options:[{value:"",label:"Select a budget…"},...n.map(p=>({value:p.budget_id,label:E(p)}))]}),e.jsx(A,{error:o})]}),e.jsxs(f.Footer,{children:[e.jsx(d,{variant:"ghost",isDisabled:i,onPress:()=>l(!1),children:"Cancel"}),e.jsx(d,{variant:"primary",isDisabled:!c,isPending:i,onPress:()=>u(c),children:"Assign"})]})]})})}):null})}function Ae(){const t=Q(),l=M(),n=K(),a=X(),[i,o]=r.useState(!1),[u,c]=r.useState(null),[g,p]=r.useState(!1),m=t.data??[],b=t.isLoading,j=m.filter(s=>D(s.user_id)).length,x=g?m:m.filter(s=>!D(s.user_id)),h=m.find(s=>s.user_id===u)??null,C=!b&&x.length===0&&!i,P=r.useMemo(()=>new Map((l.data??[]).map(s=>[s.budget_id,s])),[l.data]),y=te(),[q,_]=r.useState(!1),[W,N]=r.useState(!1),[F,z]=r.useState(void 0),[T,L]=r.useState(!1),G=x.map(s=>s.user_id),v=ae(y.selectedKeys,G),O=r.useCallback((s,k)=>n.mutate({id:s.user_id,body:{blocked:k}}),[n.mutate]),V=async(s,k)=>{L(!0),z(void 0);try{for(const w of v)await s(w);y.clear(),k()}catch(w){z(w)}finally{L(!1)}},J=r.useMemo(()=>[{id:"user",header:"User",isRowHeader:!0,cell:s=>e.jsxs("div",{className:"flex flex-col gap-0.5",children:[e.jsxs("span",{className:"inline-flex items-center gap-1.5",children:[e.jsx("code",{className:"text-xs font-medium text-[var(--otari-ink)]",children:s.user_id}),D(s.user_id)?e.jsx(I,{size:"sm",color:"default",children:"virtual"}):null]}),s.alias?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:s.alias}):null]})},{id:"status",header:"Status",cell:s=>e.jsx(me,{user:s})},{id:"budget",header:"Budget",cell:s=>s.budget_id?e.jsx("span",{className:"text-[var(--otari-muted)]",title:s.budget_id,children:P.get(s.budget_id)?E(P.get(s.budget_id)):H(s.budget_id)}):e.jsx("span",{className:"text-[var(--otari-muted)]",children:"—"})},{id:"spend",header:"Spend",cell:s=>e.jsxs("span",{className:"text-[var(--otari-muted)]",children:[U(s.spend),s.reserved>0?e.jsxs("span",{children:[" (+",U(s.reserved)," held)"]}):null]})},{id:"access",header:"Model access",cell:s=>e.jsx(xe,{allowed:s.allowed_models})},{id:"actions",header:"Actions",align:"end",cell:s=>e.jsxs("div",{className:"flex items-center justify-end gap-1.5",children:[e.jsx(d,{size:"sm",variant:"outline",isDisabled:n.isPending,onPress:()=>O(s,!s.blocked),children:s.blocked?"Unblock":"Block"}),e.jsx(d,{size:"sm",variant:"ghost",onPress:()=>{o(!1),c(s.user_id)},children:"Edit"}),e.jsx(ge,{trigger:"Delete",confirmLabel:"Delete user",isPending:a.isPending,message:e.jsxs(e.Fragment,{children:["Delete ",e.jsx("strong",{children:s.user_id}),"? This deactivates its API keys and hides the user; usage history is preserved."]}),onConfirm:()=>a.mutate(s.user_id)})]})}],[P,n.isPending,a.isPending,a.mutate,O]);return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(Y,{title:"Users",description:"People and teams that own API keys. Set each one's budget and default model access here; issue their keys on the API keys page.",action:i?null:e.jsx(d,{variant:"primary",onPress:()=>{c(null),o(!0)},children:"Create user"})}),e.jsx(A,{error:t.error??n.error??a.error}),C?e.jsx(Z,{title:"No users yet",description:"A user owns API keys and carries the budget and default model access those keys inherit. Create a user here, then issue its keys on the API keys page.",actionLabel:"Create your first user",onAction:()=>{c(null),o(!0)}}):null,j>0?e.jsxs("label",{className:"flex w-fit items-center gap-2 text-xs text-[var(--otari-muted)]",children:[e.jsx("input",{type:"checkbox",checked:g,onChange:s=>p(s.target.checked)}),"Show auto-created (virtual) users (",j,")"]}):null,i?e.jsx(ce,{onClose:()=>o(!1)}):null,h?e.jsx(ue,{user:h,onClose:()=>c(null)},h.user_id):null,v.length>0?e.jsxs(ne,{selectedCount:v.length,allMatching:!1,matchingTotal:null,canSelectAllMatching:!1,onSelectAllMatching:()=>{},onClear:y.clear,children:[e.jsx(d,{size:"sm",variant:"primary",onPress:()=>N(!0),children:"Assign budget"}),e.jsx(d,{size:"sm",variant:"danger",onPress:()=>_(!0),children:"Delete"})]}):null,C?null:e.jsx(le,{ariaLabel:"Users",columns:J,rows:x,getRowKey:oe,isLoading:b,emptyContent:"No users yet. Create one, or create an API key to auto-create one.",selectionMode:"multiple",selectedKeys:y.selectedKeys,onSelectionChange:y.onSelectionChange}),e.jsx(re,{isOpen:q,onOpenChange:_,heading:"Delete users",body:`Delete ${v.length} ${v.length===1?"user":"users"}? This deactivates their API keys and hides them; usage history is preserved.`,confirmLabel:"Delete",isPending:T,error:F,onConfirm:()=>V(s=>a.mutateAsync(s),()=>_(!1))}),e.jsx(he,{isOpen:W,onOpenChange:N,budgets:l.data??[],count:v.length,isPending:T,error:F,onAssign:s=>V(k=>n.mutateAsync({id:k,body:{budget_id:s}}),()=>N(!1))})]})}export{Ae as UsersPage}; +import{j as e}from"./tanstack-query-1t81HyiD.js";import{r}from"./react-dgEcD0HR.js";import{u as Q,q as M,v as K,aq as X,P as Y,E as A,w as Z,ar as ee,F as se}from"./index-Br3lxdY5.js";import{u as te,r as ae,B as ne}from"./tableSelection-9hV37uhu.js";import{C as re}from"./ConfirmDialog-KQrh5qGO.js";import{D as le}from"./DataTable-Bmn_eYSV.js";import{F as B}from"./Field-Dwp6u3t8.js";import{a as ie,M as R}from"./ModelScopeControl-DYPdGezt.js";import{f as I,B as d,d as S,A as f}from"./heroui-BI50yK5B.js";const de=new Intl.NumberFormat(void 0,{style:"currency",currency:"USD",maximumFractionDigits:4});function U(t){return de.format(t)}const oe=t=>t.user_id,D=t=>t.startsWith("apikey-");function H(t){return t.split("-")[0]}function E(t){return t.name??H(t.budget_id)}function $({value:t,onChange:l,budgets:n}){return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("label",{htmlFor:"user-budget",className:"text-sm font-medium text-[var(--otari-ink)]",children:"Budget"}),e.jsxs("select",{id:"user-budget",value:t??"",onChange:a=>l(a.target.value||null),className:"w-full rounded-lg border border-[var(--otari-line)] bg-[var(--otari-bg)] px-3 py-2 text-sm text-[var(--otari-ink)]",children:[e.jsx("option",{value:"",children:"No budget (unlimited)"}),n.map(a=>e.jsxs("option",{value:a.budget_id,children:[E(a),a.max_budget===null?" · no limit":` · ${U(a.max_budget)}`]},a.budget_id))]}),e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:"The spending limit this user is held to. Manage budgets on the Budgets page."})]})}function ce({onClose:t}){const l=ee(),n=M(),[a,i]=r.useState(""),[o,u]=r.useState(""),[c,g]=r.useState(null),[p,m]=r.useState(null),[b,j]=r.useState(!0),x=()=>{if(l.isPending||!b||a.trim()==="")return;const h={user_id:a.trim(),alias:o.trim()||null,budget_id:c,allowed_models:p};l.mutate(h,{onSuccess:t})};return e.jsx(S,{children:e.jsxs(S.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsx("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:"Create user"}),e.jsx(A,{error:l.error}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(B,{label:"User ID",value:a,onChange:i,placeholder:"alice@example.com",isRequired:!0,autoFocus:!0,description:"The identifier callers send as the `user` field; spend and budgets track against it."}),e.jsx(B,{label:"Alias (optional)",value:o,onChange:u,placeholder:"Alice"})]}),e.jsx($,{value:c,onChange:g,budgets:n.data??[]}),e.jsx(R,{title:"Model access (default for this user's keys)",description:"The models this user's keys may list and call by default. A key can narrow this, but never exceed it.",initial:null,onChange:(h,C)=>{m(h),j(C)}}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(d,{variant:"primary",isDisabled:l.isPending||!b||a.trim()==="",onPress:x,children:l.isPending?"Creating…":"Create user"}),e.jsx(d,{variant:"ghost",onPress:t,children:"Cancel"})]})]})})}function ue({user:t,onClose:l}){const n=K(),a=M(),[i,o]=r.useState(t.alias??""),[u,c]=r.useState(t.budget_id),[g,p]=r.useState(t.allowed_models),[m,b]=r.useState(!0),j=()=>{if(n.isPending||!m)return;const x={alias:i.trim()||null,budget_id:u,allowed_models:g};n.mutate({id:t.user_id,body:x},{onSuccess:l})};return e.jsx(S,{children:e.jsxs(S.Content,{className:"flex flex-col gap-4 p-5",children:[e.jsxs("div",{className:"text-sm font-semibold text-[var(--otari-ink)]",children:["Edit ",e.jsx("code",{children:t.user_id})]}),e.jsx(A,{error:n.error}),e.jsx(B,{label:"Alias",value:i,onChange:o,placeholder:"Alice"}),e.jsx($,{value:u,onChange:c,budgets:a.data??[]}),e.jsx(R,{title:"Model access (default for this user's keys)",description:"The models this user's keys may list and call by default. A key can narrow this, but never exceed it.",initial:t.allowed_models,onChange:(x,h)=>{p(x),b(h)}}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(d,{variant:"primary",isDisabled:n.isPending||!m,onPress:j,children:n.isPending?"Saving…":"Save changes"}),e.jsx(d,{variant:"ghost",onPress:l,children:"Cancel"})]})]})})}function ge({trigger:t,message:l,confirmLabel:n,isPending:a,onConfirm:i}){const[o,u]=r.useState(!1);return o?e.jsxs("div",{className:"flex flex-col items-end gap-1.5 rounded-lg border border-amber-200 bg-amber-50 p-2 text-right",children:[e.jsx("span",{className:"max-w-xs text-xs text-amber-800",children:l}),e.jsxs("span",{className:"inline-flex gap-1",children:[e.jsx(d,{size:"sm",variant:"danger",isDisabled:a,onPress:i,children:n}),e.jsx(d,{size:"sm",variant:"ghost",isDisabled:a,onPress:()=>u(!1),children:"Cancel"})]})]}):e.jsx(d,{size:"sm",variant:"danger-soft",onPress:()=>u(!0),children:t})}function me({user:t}){return t.blocked?e.jsx(I,{size:"sm",color:"warning",children:"Blocked"}):e.jsx(I,{size:"sm",color:"accent",children:"Active"})}function xe({allowed:t}){const{text:l,tone:n}=ie(t),a=n==="danger"?"text-red-700 font-medium":n==="muted"?"text-[var(--otari-muted)]":"text-[var(--otari-brand-dark)] font-medium",i=t&&t.length>0?t.join(", "):void 0;return e.jsx("span",{className:`text-xs ${a}`,title:i,children:l})}function he({isOpen:t,onOpenChange:l,budgets:n,count:a,isPending:i,error:o,onAssign:u}){const[c,g]=r.useState("");return r.useEffect(()=>{t&&g("")},[t]),e.jsx(f,{isOpen:t,onOpenChange:l,children:t?e.jsx(f.Backdrop,{children:e.jsx(f.Container,{placement:"center",size:"md",children:e.jsxs(f.Dialog,{children:[e.jsx(f.Header,{children:e.jsx(f.Heading,{children:"Assign budget"})}),e.jsxs(f.Body,{className:"flex flex-col gap-4",children:[e.jsxs("p",{className:"text-sm text-[var(--otari-muted)]",children:["Assign a budget to ",a," selected ",a===1?"user":"users","."]}),e.jsx(se,{label:"Budget",value:c,onChange:g,options:[{value:"",label:"Select a budget…"},...n.map(p=>({value:p.budget_id,label:E(p)}))]}),e.jsx(A,{error:o})]}),e.jsxs(f.Footer,{children:[e.jsx(d,{variant:"ghost",isDisabled:i,onPress:()=>l(!1),children:"Cancel"}),e.jsx(d,{variant:"primary",isDisabled:!c,isPending:i,onPress:()=>u(c),children:"Assign"})]})]})})}):null})}function Ae(){const t=Q(),l=M(),n=K(),a=X(),[i,o]=r.useState(!1),[u,c]=r.useState(null),[g,p]=r.useState(!1),m=t.data??[],b=t.isLoading,j=m.filter(s=>D(s.user_id)).length,x=g?m:m.filter(s=>!D(s.user_id)),h=m.find(s=>s.user_id===u)??null,C=!b&&x.length===0&&!i,P=r.useMemo(()=>new Map((l.data??[]).map(s=>[s.budget_id,s])),[l.data]),y=te(),[q,_]=r.useState(!1),[W,N]=r.useState(!1),[F,z]=r.useState(void 0),[T,L]=r.useState(!1),G=x.map(s=>s.user_id),v=ae(y.selectedKeys,G),O=r.useCallback((s,k)=>n.mutate({id:s.user_id,body:{blocked:k}}),[n.mutate]),V=async(s,k)=>{L(!0),z(void 0);try{for(const w of v)await s(w);y.clear(),k()}catch(w){z(w)}finally{L(!1)}},J=r.useMemo(()=>[{id:"user",header:"User",isRowHeader:!0,cell:s=>e.jsxs("div",{className:"flex flex-col gap-0.5",children:[e.jsxs("span",{className:"inline-flex items-center gap-1.5",children:[e.jsx("code",{className:"text-xs font-medium text-[var(--otari-ink)]",children:s.user_id}),D(s.user_id)?e.jsx(I,{size:"sm",color:"default",children:"virtual"}):null]}),s.alias?e.jsx("span",{className:"text-xs text-[var(--otari-muted)]",children:s.alias}):null]})},{id:"status",header:"Status",cell:s=>e.jsx(me,{user:s})},{id:"budget",header:"Budget",cell:s=>s.budget_id?e.jsx("span",{className:"text-[var(--otari-muted)]",title:s.budget_id,children:P.get(s.budget_id)?E(P.get(s.budget_id)):H(s.budget_id)}):e.jsx("span",{className:"text-[var(--otari-muted)]",children:"—"})},{id:"spend",header:"Spend",cell:s=>e.jsxs("span",{className:"text-[var(--otari-muted)]",children:[U(s.spend),s.reserved>0?e.jsxs("span",{children:[" (+",U(s.reserved)," held)"]}):null]})},{id:"access",header:"Model access",cell:s=>e.jsx(xe,{allowed:s.allowed_models})},{id:"actions",header:"Actions",align:"end",cell:s=>e.jsxs("div",{className:"flex items-center justify-end gap-1.5",children:[e.jsx(d,{size:"sm",variant:"outline",isDisabled:n.isPending,onPress:()=>O(s,!s.blocked),children:s.blocked?"Unblock":"Block"}),e.jsx(d,{size:"sm",variant:"ghost",onPress:()=>{o(!1),c(s.user_id)},children:"Edit"}),e.jsx(ge,{trigger:"Delete",confirmLabel:"Delete user",isPending:a.isPending,message:e.jsxs(e.Fragment,{children:["Delete ",e.jsx("strong",{children:s.user_id}),"? This deactivates its API keys and hides the user; usage history is preserved."]}),onConfirm:()=>a.mutate(s.user_id)})]})}],[P,n.isPending,a.isPending,a.mutate,O]);return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(Y,{title:"Users",description:"People and teams that own API keys. Set each one's budget and default model access here; issue their keys on the API keys page.",action:i?null:e.jsx(d,{variant:"primary",onPress:()=>{c(null),o(!0)},children:"Create user"})}),e.jsx(A,{error:t.error??n.error??a.error}),C?e.jsx(Z,{title:"No users yet",description:"A user owns API keys and carries the budget and default model access those keys inherit. Create a user here, then issue its keys on the API keys page.",actionLabel:"Create your first user",onAction:()=>{c(null),o(!0)}}):null,j>0?e.jsxs("label",{className:"flex w-fit items-center gap-2 text-xs text-[var(--otari-muted)]",children:[e.jsx("input",{type:"checkbox",checked:g,onChange:s=>p(s.target.checked)}),"Show auto-created (virtual) users (",j,")"]}):null,i?e.jsx(ce,{onClose:()=>o(!1)}):null,h?e.jsx(ue,{user:h,onClose:()=>c(null)},h.user_id):null,v.length>0?e.jsxs(ne,{selectedCount:v.length,allMatching:!1,matchingTotal:null,canSelectAllMatching:!1,onSelectAllMatching:()=>{},onClear:y.clear,children:[e.jsx(d,{size:"sm",variant:"primary",onPress:()=>N(!0),children:"Assign budget"}),e.jsx(d,{size:"sm",variant:"danger",onPress:()=>_(!0),children:"Delete"})]}):null,C?null:e.jsx(le,{ariaLabel:"Users",columns:J,rows:x,getRowKey:oe,isLoading:b,emptyContent:"No users yet. Create one, or create an API key to auto-create one.",selectionMode:"multiple",selectedKeys:y.selectedKeys,onSelectionChange:y.onSelectionChange}),e.jsx(re,{isOpen:q,onOpenChange:_,heading:"Delete users",body:`Delete ${v.length} ${v.length===1?"user":"users"}? This deactivates their API keys and hides them; usage history is preserved.`,confirmLabel:"Delete",isPending:T,error:F,onConfirm:()=>V(s=>a.mutateAsync(s),()=>_(!1))}),e.jsx(he,{isOpen:W,onOpenChange:N,budgets:l.data??[],count:v.length,isPending:T,error:F,onAssign:s=>V(k=>n.mutateAsync({id:k,body:{budget_id:s}}),()=>N(!1))})]})}export{Ae as UsersPage}; diff --git a/src/gateway/static/dashboard/assets/index-Br3lxdY5.js b/src/gateway/static/dashboard/assets/index-Br3lxdY5.js new file mode 100644 index 00000000..c2195752 --- /dev/null +++ b/src/gateway/static/dashboard/assets/index-Br3lxdY5.js @@ -0,0 +1,2 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ActivityPage-D2jsRt9a.js","assets/tanstack-query-1t81HyiD.js","assets/react-dgEcD0HR.js","assets/FilterChips-BbDynOdb.js","assets/heroui-BI50yK5B.js","assets/recharts-C4kRDmvS.js","assets/tableSelection-9hV37uhu.js","assets/ConfirmDialog-KQrh5qGO.js","assets/DataTable-Bmn_eYSV.js","assets/TablePagination-BR55oZ8k.js","assets/AliasesPage-CrabMwlU.js","assets/Field-Dwp6u3t8.js","assets/BudgetsPage-DjVks5Lb.js","assets/DocsPage-Bo_X9aMz.js","assets/KeysPage--hq_RPOL.js","assets/ModelScopeControl-DYPdGezt.js","assets/ModelsPage-Dt9ZAYGy.js","assets/OverviewPage-DPI1n-nt.js","assets/charts-DIsd9m--.js","assets/ProvidersPage-CC2Glu9P.js","assets/SettingsPage-CYDMBdEO.js","assets/ToolsGuardrailsPage-B68S03Js.js","assets/UsagePage-BYqtE3tC.js","assets/UsersPage-CZXIBvdD.js"])))=>i.map(i=>d[i]); +var Ie=Object.defineProperty;var qe=(e,t,r)=>t in e?Ie(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var ce=(e,t,r)=>qe(e,typeof t!="symbol"?t+"":t,r);import{u as g,j as n,a as p,b as x,k as ne,Q as Re,c as Oe}from"./tanstack-query-1t81HyiD.js";import{d as Me,r as o,N as $,O as Fe,H as Ue,e as Ke,f as w,h as Be}from"./react-dgEcD0HR.js";import{B as C,C as U,L as xe,I as ye,a as $e,b as ze,d as I,S as Qe,c as T,T as Ve,e as We}from"./heroui-BI50yK5B.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))s(a);new MutationObserver(a=>{for(const i of a)if(i.type==="childList")for(const u of i.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&s(u)}).observe(document,{childList:!0,subtree:!0});function r(a){const i={};return a.integrity&&(i.integrity=a.integrity),a.referrerPolicy&&(i.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?i.credentials="include":a.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function s(a){if(a.ep)return;a.ep=!0;const i=r(a);fetch(a.href,i)}})();var He=Me();const Ge="modulepreload",Je=function(e){return"/"+e},ue={},P=function(t,r,s){let a=Promise.resolve();if(r&&r.length>0){let u=function(y){return Promise.all(y.map(h=>Promise.resolve(h).then(b=>({status:"fulfilled",value:b}),b=>({status:"rejected",reason:b}))))};document.getElementsByTagName("link");const d=document.querySelector("meta[property=csp-nonce]"),f=(d==null?void 0:d.nonce)||(d==null?void 0:d.getAttribute("nonce"));a=u(r.map(y=>{if(y=Je(y),y in ue)return;ue[y]=!0;const h=y.endsWith(".css"),b=h?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${y}"]${b}`))return;const m=document.createElement("link");if(m.rel=h?"stylesheet":Ge,h||(m.as="script"),m.crossOrigin="",m.href=y,f&&m.setAttribute("nonce",f),document.head.appendChild(m),h)return new Promise((N,M)=>{m.addEventListener("load",N),m.addEventListener("error",()=>M(new Error(`Unable to preload CSS for ${y}`)))})}))}function i(u){const d=new Event("vite:preloadError",{cancelable:!0});if(d.payload=u,window.dispatchEvent(d),!d.defaultPrevented)throw u}return a.then(u=>{for(const d of u||[])d.status==="rejected"&&i(d.reason);return t().catch(i)})};class L extends Error{constructor(r,s){super(s);ce(this,"status");this.name="ApiError",this.status=r}}let K=null;function de(e){K=e}async function ee(e){try{const t=await e.json();if(typeof t.detail=="string")return t.detail;if(t.detail!=null)return JSON.stringify(t.detail)}catch{}return e.statusText||`Request failed (${e.status})`}async function Ye(e){let t;try{t=await fetch("/v1/auth/session",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({master_key:e})})}catch{throw new L(0,"Network error: could not reach the gateway.")}if(t.status===401||t.status===403)return!1;if(!t.ok)throw new L(t.status,await ee(t));return!0}async function Xe(){try{await fetch("/v1/auth/session",{method:"DELETE"})}catch{}}async function c(e,t={}){const r=new Headers(t.headers);r.set("Accept","application/json"),t.body!=null&&!r.has("Content-Type")&&r.set("Content-Type","application/json");let s;try{s=await fetch(e,{...t,headers:r})}catch{throw new L(0,"Network error: could not reach the gateway.")}if(s.status===401||s.status===403)throw K==null||K(),new L(s.status,await ee(s));if(!s.ok)throw new L(s.status,await ee(s));if(s.status!==204)return await s.json()}const te="otari.dashboard.hasSession",ve=o.createContext(null);function Ze(){try{return window.localStorage.getItem(te)==="1"}catch{return!1}}function et({children:e}){const t=g(),[r,s]=o.useState(Ze),a=o.useCallback(()=>{Xe(),s(!1),t.clear();try{window.localStorage.removeItem(te)}catch{}},[t]),i=o.useCallback(()=>{t.clear(),s(!0);try{window.localStorage.setItem(te,"1")}catch{}},[t]);o.useEffect(()=>(de(a),()=>de(null)),[a]);const u=o.useMemo(()=>({isAuthenticated:r,login:i,logout:a}),[r,i,a]);return n.jsx(ve.Provider,{value:u,children:e})}function re(){const e=o.useContext(ve);if(!e)throw new Error("useAuth must be used within an AuthProvider");return e}function tt(e){return e instanceof L&&e.status===0}function nt(){const e=g(),[t,r]=o.useState(!1);return o.useEffect(()=>{const s=e.getQueryCache(),a=()=>s.getAll().some(i=>i.state.status==="error"&&tt(i.state.error));return r(a()),s.subscribe(()=>r(a()))},[e]),t}function rt(){return nt()?n.jsxs("div",{role:"alert","aria-live":"assertive",className:"fixed right-4 bottom-4 z-50 flex max-w-sm items-start gap-2.5 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 shadow-lg",children:[n.jsxs("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,className:"mt-0.5 h-5 w-5 shrink-0",children:[n.jsx("path",{d:"M12 9v4M12 17h.01",strokeLinecap:"round",strokeLinejoin:"round"}),n.jsx("path",{d:"M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z",strokeLinejoin:"round"})]}),n.jsxs("span",{children:[n.jsx("strong",{className:"font-semibold",children:"Can’t reach the gateway."})," The backend isn’t responding; data won’t load or save until the connection is restored."]})]}):null}const z=3600,_=86400,st=366,un=[{key:"1h",label:"Last hour",seconds:z,bucket:"hour"},{key:"24h",label:"24h",seconds:_,bucket:"hour"},{key:"7d",label:"7d",seconds:7*_,bucket:"day"},{key:"30d",label:"30d",seconds:30*_,bucket:"day"},{key:"90d",label:"90d",seconds:90*_,bucket:"day"},{key:"12mo",label:"12mo",seconds:st*_,bucket:"day"}],dn="30d",mn=[{key:"1h",label:"1h",seconds:z,bucket:"hour"},{key:"24h",label:"24h",seconds:_,bucket:"hour"},{key:"7d",label:"7d",seconds:7*_,bucket:"day"},{key:"30d",label:"30d",seconds:30*_,bucket:"day"},{key:"all",label:"All",seconds:null,bucket:"day"}],fn="24h",hn="custom";function xn(e,t){return e.find(r=>r.key===t)}function at(e,t=Date.now()){return new Date(t-e*1e3).toISOString()}function it(e){return(e==="hour"?z:_)*1e3}function yn(e,t,r=Date.now()){const s=new Date(e).getTime();return(t?new Date(t).getTime():r)-s<=_*1e3?"hour":"day"}function vn(e,t,r,s){if(e.length===0)return null;const a=Math.max(0,Math.min(t,r)),i=Math.min(e.length-1,Math.max(t,r)),u=new Date(e[a]).getTime(),d=new Date(e[i]).getTime()+it(s);return{startIso:new Date(u).toISOString(),endIso:new Date(d).toISOString()}}function gn(e,t,r){const s=e.length;if(s===0)return{startIndex:0,endIndex:0};const a=e.map(d=>new Date(d).getTime());let i=0;if(t){const d=new Date(t).getTime();for(let f=0;fc("/v1/models"),staleTime:6e4})}function ut(){return p({queryKey:[lt],queryFn:()=>c("/dashboard-build.json"),refetchInterval:ct,refetchOnWindowFocus:!0,staleTime:0,retry:!1})}function jn(){return p({queryKey:[ae],queryFn:()=>c("/v1/models/discoverable"),staleTime:5*6e4})}function wn(){return p({queryKey:[ie],queryFn:()=>c("/v1/providers"),staleTime:5*6e4})}function Sn(){return p({queryKey:["provider-catalog"],queryFn:()=>c("/v1/providers/catalog"),staleTime:1/0})}function kn(e){return p({queryKey:["provider-catalog",e],queryFn:()=>c(`/v1/providers/catalog/${encodeURIComponent(e)}`),enabled:e!=="",staleTime:1/0})}function En(){return p({queryKey:[oe],queryFn:()=>c("/v1/providers/health"),staleTime:me,refetchInterval:me})}function Pn(){const e=g();return x({mutationFn:()=>c("/v1/providers/health?refresh=true"),onSuccess:t=>e.setQueryData([oe],t)})}function Nn(){return p({queryKey:[be],queryFn:()=>c("/v1/provider-credentials"),staleTime:6e4})}function V(e){e.invalidateQueries({queryKey:[be]}),e.invalidateQueries({queryKey:[ie]}),e.invalidateQueries({queryKey:[D]}),e.invalidateQueries({queryKey:[ae]}),e.invalidateQueries({queryKey:[oe]})}function Cn(){const e=g();return x({mutationFn:t=>c("/v1/provider-credentials",{method:"POST",body:JSON.stringify(t)}),onSuccess:()=>V(e)})}function Tn(){const e=g();return x({mutationFn:({instance:t,body:r})=>c(`/v1/provider-credentials/${encodeURIComponent(t)}`,{method:"PATCH",body:JSON.stringify(r)}),onSuccess:()=>V(e)})}function _n(){const e=g();return x({mutationFn:t=>c(`/v1/provider-credentials/${encodeURIComponent(t)}`,{method:"DELETE"}),onSuccess:()=>V(e)})}function Ln(){const e=g();return x({mutationFn:()=>c("/v1/provider-credentials/reencrypt",{method:"POST"}),onSuccess:()=>V(e)})}function Dn(){return x({mutationFn:e=>c(`/v1/provider-credentials/${encodeURIComponent(e)}/test`,{method:"POST"})})}function An(){return x({mutationFn:e=>c("/v1/provider-credentials/test",{method:"POST",body:JSON.stringify(e)})})}function In(){return p({queryKey:[ot],queryFn:()=>c("/v1/models/metadata"),staleTime:10*6e4})}function qn(){return p({queryKey:[se],queryFn:()=>c("/v1/aliases"),staleTime:6e4})}function Rn(){const e=g();return x({mutationFn:t=>c("/v1/aliases",{method:"POST",body:JSON.stringify(t)}),onSuccess:()=>{e.invalidateQueries({queryKey:[se]}),e.invalidateQueries({queryKey:[D]})}})}function On(){const e=g();return x({mutationFn:t=>c(`/v1/aliases/${encodeURIComponent(t)}`,{method:"DELETE"}),onSuccess:()=>{e.invalidateQueries({queryKey:[se]}),e.invalidateQueries({queryKey:[D]})}})}function dt(){return p({queryKey:[ge],queryFn:()=>c("/v1/settings"),staleTime:6e4})}function mt(){const e=g();return x({mutationFn:t=>c("/v1/settings",{method:"PATCH",body:JSON.stringify(t)}),onSuccess:t=>{e.setQueryData([ge],t),e.invalidateQueries({queryKey:[D]}),e.invalidateQueries({queryKey:[ae]})}})}function Mn(){return x({mutationFn:()=>c("/v1/settings/master-key/rotate",{method:"POST"})})}function Fn(){return p({queryKey:[pe],queryFn:()=>c("/v1/tool-settings"),staleTime:6e4})}function Un(){const e=g();return x({mutationFn:t=>c("/v1/tool-settings",{method:"PATCH",body:JSON.stringify(t)}),onSuccess:t=>{e.setQueryData([pe],t)}})}function Kn(){return x({mutationFn:({service:e,url:t})=>c(`/v1/tool-settings/${encodeURIComponent(e)}/test`,{method:"POST",body:JSON.stringify({url:t})})})}const G=1e3,ft=100;async function ht(){const e=[];for(let t=0;tc("/v1/pricing",{method:"POST",body:JSON.stringify(t)}),onSuccess:()=>{e.invalidateQueries({queryKey:[Q]}),e.invalidateQueries({queryKey:[D]})}})}function zn(){const e=g();return x({mutationFn:t=>c(`/v1/pricing/${encodeURIComponent(t)}`,{method:"DELETE"}),onSuccess:()=>{e.invalidateQueries({queryKey:[Q]}),e.invalidateQueries({queryKey:[D]})}})}function Qn(){return x({mutationFn:()=>c("/v1/pricing/refresh",{method:"POST"})})}function Vn(){const e=g();return x({mutationFn:()=>c("/v1/pricing/refresh/confirm",{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:[Q]}),e.invalidateQueries({queryKey:[D]}),e.invalidateQueries({queryKey:[ie]})}})}function Wn(){return x({mutationFn:()=>c("/v1/pricing/refresh/reject",{method:"POST"})})}const J=1e3,xt=100;async function yt(){const e=[];for(let t=0;tc("/v1/keys",{method:"POST",body:JSON.stringify(t)}),onSuccess:()=>void e.invalidateQueries({queryKey:[q]})})}function Jn(){const e=g();return x({mutationFn:({id:t,body:r})=>c(`/v1/keys/${encodeURIComponent(t)}`,{method:"PATCH",body:JSON.stringify(r)}),onSuccess:()=>void e.invalidateQueries({queryKey:[q]})})}function Yn(){const e=g();return x({mutationFn:t=>c(`/v1/keys/${encodeURIComponent(t)}/rotate`,{method:"POST"}),onSuccess:()=>void e.invalidateQueries({queryKey:[q]})})}function Xn(){const e=g();return x({mutationFn:t=>c(`/v1/keys/${encodeURIComponent(t)}`,{method:"DELETE"}),onSuccess:()=>void e.invalidateQueries({queryKey:[q]})})}const Y=1e3,vt=100;async function gt(){const e=[];for(let t=0;tc(`/v1/budgets/${encodeURIComponent(e)}/reset-logs`),enabled:e!==null,staleTime:6e4})}function tr(){const e=g();return x({mutationFn:t=>c("/v1/budgets",{method:"POST",body:JSON.stringify(t)}),onSuccess:()=>void e.invalidateQueries({queryKey:[R]})})}function nr(){const e=g();return x({mutationFn:({id:t,body:r})=>c(`/v1/budgets/${encodeURIComponent(t)}`,{method:"PATCH",body:JSON.stringify(r)}),onSuccess:()=>void e.invalidateQueries({queryKey:[R]})})}function rr(){const e=g();return x({mutationFn:t=>c(`/v1/budgets/${encodeURIComponent(t)}`,{method:"DELETE"}),onSuccess:()=>void e.invalidateQueries({queryKey:[R]})})}const X=1e3,pt=100;async function bt(){const e=[];for(let t=0;tc("/v1/users",{method:"POST",body:JSON.stringify(t)}),onSuccess:()=>le(e)})}function ir(){const e=g();return x({mutationFn:({id:t,body:r})=>c(`/v1/users/${encodeURIComponent(t)}`,{method:"PATCH",body:JSON.stringify(r)}),onSuccess:()=>le(e)})}function or(){const e=g();return x({mutationFn:t=>c(`/v1/users/${encodeURIComponent(t)}`,{method:"DELETE"}),onSuccess:()=>{le(e),e.invalidateQueries({queryKey:[q]})}})}function W(e){const t=new URLSearchParams;return e.start_date&&t.set("start_date",e.start_date),e.end_date&&t.set("end_date",e.end_date),e.status&&t.set("status",e.status),e.model&&t.set("model",e.model),e.endpoint&&t.set("endpoint",e.endpoint),e.user_id&&t.set("user_id",e.user_id),e.api_key_id&&t.set("api_key_id",e.api_key_id),e.source&&t.set("source",e.source),e.priced!==void 0&&t.set("priced",String(e.priced)),e.counts_toward_budget!==void 0&&t.set("counts_toward_budget",String(e.counts_toward_budget)),t}function lr(e,t,r){return p({queryKey:[O,"list",e,t,r],queryFn:()=>{const s=W(e);return s.set("skip",String(t*r)),s.set("limit",String(r)),c(`/v1/usage?${s.toString()}`)},placeholderData:ne,staleTime:1e4})}function cr(e,t=!0){return p({queryKey:[O,"count",e],queryFn:()=>c(`/v1/usage/count?${W(e).toString()}`),enabled:t,placeholderData:ne,staleTime:1e4})}const jt=6e4;function wt(e,t=!0){return p({queryKey:[O,"count","failures",e],queryFn:()=>{const r={status:"error",start_date:at(e)};return c(`/v1/usage/count?${W(r).toString()}`)},enabled:t,refetchInterval:jt,refetchOnWindowFocus:!0,staleTime:0,retry:!1})}function ur(){const e=g();return x({mutationFn:t=>c("/v1/usage",{method:"DELETE",body:JSON.stringify(t)}),onSuccess:()=>{e.invalidateQueries({queryKey:[O]})}})}function dr(){const e=g();return x({mutationFn:t=>c("/v1/usage/set-price",{method:"POST",body:JSON.stringify(t)}),onSuccess:()=>{e.invalidateQueries({queryKey:[O]})}})}function mr(e,t,r=!0){return p({queryKey:[O,"summary",e,t],queryFn:()=>{const s=W(e);return s.set("bucket",t),c(`/v1/usage/summary?${s.toString()}`)},enabled:r,placeholderData:ne,staleTime:3e4})}function fr(e){return e==null?"0":new Intl.NumberFormat("en-US").format(e)}function hr(e){if(e==null)return"$0.00";const t=e!==0&&Math.abs(e)<.01?4:2;return new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",minimumFractionDigits:2,maximumFractionDigits:t}).format(e)}function xr(e){if(e==null)return"—";if(e>=1e6){const t=e/1e6;return`${Number.isInteger(t)?t:t.toFixed(1)}M`}if(e>=1e3){const t=Math.round(e/1e3);return t>=1e3?"1M":`${t}K`}return String(e)}const St=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function yr(e){if(!e)return"—";const t=/^(\d{4})-(\d{2})/.exec(e);if(!t)return e;const r=Number(t[2])-1;return r<0||r>11?t[1]:`${St[r]} ${t[1]}`}const kt=new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",maximumFractionDigits:2});function vr(e){return kt.format(e)}function gr(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function Et(e){return`${(e*100).toFixed(1)}%`}function pr(e,t){return t===void 0||t===0?null:(e-t)/t}function Pt(e,t=Date.now()){if(!e)return"never";const r=new Date(e);if(Number.isNaN(r.getTime()))return e;const s=Math.round((t-r.getTime())/1e3),a=s<0,i=Math.abs(s),u=[["second",60],["minute",60],["hour",24],["day",30],["month",12],["year",Number.POSITIVE_INFINITY]];let d=i,f="second";for(const[h,b]of u){if(f=h,d0?"▲":e<0?"▼":"•";return n.jsxs("span",{className:"text-[var(--otari-muted)]",children:[t," ",Et(Math.abs(e))," vs prev"]})}function Nt(e){return e instanceof L||e instanceof Error?e.message:"Something went wrong."}function Ct({error:e}){return e?n.jsx("div",{role:"alert",className:"rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700",children:Nt(e)}):null}function Tt({tone:e="info",children:t}){const r=e==="warning"?"border-amber-200 bg-amber-50 text-amber-800":"border-[var(--otari-brand)] bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]";return n.jsx("div",{className:`rounded-lg border px-4 py-3 text-sm ${r}`,children:t})}function wr({title:e,description:t,action:r}){return n.jsxs("div",{className:"flex flex-col gap-3",children:[n.jsxs("div",{children:[n.jsx("h1",{className:"text-xl font-semibold text-[var(--otari-ink)]",children:e}),t?n.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:t}):null]}),r?n.jsx("div",{className:"flex flex-wrap gap-2",children:r}):null]})}function _t(e){const[t,r]=o.useState(()=>Date.now());return o.useEffect(()=>{let s;const a=()=>{s===void 0&&(s=setInterval(()=>r(Date.now()),e))},i=()=>{s!==void 0&&(clearInterval(s),s=void 0)},u=()=>{r(Date.now()),document.visibilityState==="visible"?a():i()};return u(),document.addEventListener("visibilitychange",u),()=>{i(),document.removeEventListener("visibilitychange",u)}},[e]),t}function Sr({onRefresh:e,isFetching:t=!1,updatedAt:r,label:s="Refresh"}){const a=_t(15e3),i=r?Pt(new Date(r).toISOString(),a):null;return n.jsxs("span",{className:"inline-flex items-center gap-2",children:[i?n.jsxs("span",{className:"text-xs text-[var(--otari-muted)]",children:["Updated ",i]}):null,n.jsx(C,{variant:"outline",size:"sm",isIconOnly:!0,isDisabled:t,onPress:e,"aria-label":s,children:n.jsxs("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:`h-4 w-4 ${t?"animate-spin":""}`,"aria-hidden":"true",children:[n.jsx("path",{d:"M20 11a8 8 0 1 0-.5 4",strokeLinecap:"round",strokeLinejoin:"round"}),n.jsx("path",{d:"M20 4v5h-5",strokeLinecap:"round",strokeLinejoin:"round"})]})})]})}function kr({title:e,description:t,actionLabel:r,onAction:s,isActionDisabled:a,children:i}){return n.jsx(I,{children:n.jsxs(I.Content,{className:"flex flex-col gap-4 p-6",children:[n.jsxs("div",{children:[n.jsx("h2",{className:"text-lg font-semibold text-[var(--otari-ink)]",children:e}),t?n.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:t}):null]}),i,r&&s?n.jsx("div",{children:n.jsx(C,{variant:"primary",isDisabled:a,onPress:s,children:r})}):null]})})}function Er({label:e="Loading…"}){return n.jsxs("div",{role:"status",className:"flex items-center justify-center gap-2 px-4 py-10 text-sm text-[var(--otari-muted)]",children:[n.jsx(Qe,{size:"sm"}),n.jsx("span",{children:e})]})}function Pr({children:e,confirmLabel:t,onConfirm:r,isPending:s}){const[a,i]=o.useState(!1);return a?n.jsxs("span",{className:"inline-flex items-center gap-1",children:[n.jsx(C,{size:"sm",variant:"danger",isDisabled:s,onPress:r,children:t}),n.jsx(C,{size:"sm",variant:"ghost",isDisabled:s,onPress:()=>i(!1),children:"Cancel"})]}):n.jsx(C,{size:"sm",variant:"danger-soft",onPress:()=>i(!0),children:e})}const Lt="rounded-lg border border-[var(--otari-line)] bg-[var(--otari-bg)] px-3 py-2 text-sm text-[var(--otari-ink)] focus:border-[var(--otari-brand)] focus:outline-none";function Nr({id:e,label:t,ariaLabel:r,value:s,onChange:a,options:i,children:u,disabled:d}){const f=o.useId(),y=e??(t?f:void 0),h=n.jsx("select",{id:y,"aria-label":t?void 0:r,value:s,disabled:d,onChange:b=>a(b.target.value),className:Lt,children:i?i.map(b=>n.jsx("option",{value:b.value,children:b.label},b.value)):u});return t?n.jsxs("div",{className:"flex flex-col gap-1",children:[n.jsx("label",{htmlFor:y,className:"text-xs font-medium text-[var(--otari-muted)]",children:t}),h]}):h}function Cr({label:e,value:t,onChange:r,options:s,placeholder:a,maxVisible:i=50,allowsCustom:u=!1}){const d=m=>{var N;return((N=s.find(M=>M.value===m))==null?void 0:N.label)??m},[f,y]=o.useState(()=>d(t));o.useEffect(()=>{y(d(t))},[t]);const h=f.trim().toLowerCase(),b=s.filter(m=>!h||m.value.toLowerCase().includes(h)||m.label.toLowerCase().includes(h)).slice(0,i);return n.jsxs(U.Root,{allowsEmptyCollection:!0,allowsCustomValue:u,menuTrigger:"focus",inputValue:f,onInputChange:m=>{y(m),u?r(m.trim()):m.trim()===""&&r("")},onSelectionChange:m=>{m!=null&&r(String(m))},className:"flex flex-col gap-1",children:[n.jsx(xe,{className:"text-xs font-medium text-[var(--otari-muted)]",children:e}),n.jsxs(U.InputGroup,{children:[n.jsx(ye,{placeholder:a,autoComplete:"off",onFocus:m=>m.currentTarget.select()}),n.jsx(U.Trigger,{})]}),n.jsx(U.Popover,{children:n.jsx($e,{items:b,className:"max-h-72 overflow-auto",children:m=>n.jsx(ze,{id:m.value,textValue:m.label,children:m.label})})})]})}function Dt(){var f,y;const e=dt(),t=mt(),[r,s]=o.useState(!1),i=((f=e.data)==null?void 0:f.require_pricing)===!0&&e.data.default_pricing===!1&&!r,d=((y=wt(z,i).data)==null?void 0:y.total)??0;return i?n.jsx("div",{className:"shrink-0 px-6 pt-3",children:n.jsx(Tt,{tone:"warning",children:n.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[n.jsxs("span",{children:["Requests are rejected until pricing is set (",n.jsx("code",{children:"require_pricing"})," is on). Enable default pricing to meter new models with public rates right away.",d>0?n.jsxs(n.Fragment,{children:[" ",n.jsxs("strong",{className:"font-semibold",children:[d.toLocaleString()," ",d===1?"request":"requests"," failed in the last hour."]})," ",n.jsx($,{to:"/activity?status=error&range=1h",className:"underline underline-offset-2",children:"View failed requests"})]}):null]}),n.jsxs("span",{className:"flex items-center gap-2",children:[n.jsx(C,{size:"sm",variant:"primary",isDisabled:t.isPending,onPress:()=>t.mutate({default_pricing:!0}),children:t.isPending?"Enabling…":"Enable default pricing"}),n.jsx(C,{size:"sm",variant:"ghost",onPress:()=>s(!0),children:"Dismiss"})]})]})})}):null}function At(){const{data:e}=ut(),t=o.useRef(null);return e&&t.current===null&&(t.current=e.build),e!=null&&t.current!=null&&e.build!==t.current}function It(){const e=At(),[t,r]=o.useState(!1);return!e||t?null:n.jsx("div",{className:"pointer-events-none absolute inset-x-0 top-0 z-50 flex justify-center",children:n.jsxs("div",{role:"status",className:"pointer-events-auto mt-1.5 flex items-center gap-3 rounded-full border border-[var(--otari-brand)] bg-[var(--otari-brand-tint)] py-1.5 pr-1.5 pl-4 text-sm text-[var(--otari-brand-dark)] shadow-md",children:[n.jsxs("span",{children:[n.jsx("strong",{className:"font-semibold",children:"An update is available."})," Reloading keeps you signed in."]}),n.jsx(C,{size:"sm",variant:"primary",onPress:()=>window.location.reload(),children:"Update now"}),n.jsx(C,{size:"sm",variant:"ghost",onPress:()=>r(!0),children:"Later"})]})})}const we=200,Se=480,Z=240,qt=60,ke="otari.dashboard.sidebarWidth",Ee="otari.dashboard.sidebarCollapsed",he=16,Pe="(max-width: 767px)",B=e=>Math.min(Se,Math.max(we,e));function Rt(){return typeof window>"u"||typeof window.matchMedia!="function"?!1:window.matchMedia(Pe).matches}const Ot='a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])';function Mt(e){return e?Array.from(e.querySelectorAll(Ot)).filter(t=>t.offsetParent!==null||t===document.activeElement):[]}function Ft(){if(typeof window>"u")return Z;try{const e=window.localStorage.getItem(ke),t=e?Number.parseInt(e,10):Number.NaN;return Number.isNaN(t)?Z:B(t)}catch{return Z}}function Ut(){if(typeof window>"u")return!1;try{return window.localStorage.getItem(Ee)==="1"}catch{return!1}}const Kt=[{key:"home"},{key:"observability",label:"Observability"},{key:"catalog",label:"Catalog"},{key:"access",label:"Access"},{key:"system"}],Bt=[{to:"/",section:"home",label:"Overview",end:!0,icon:n.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[n.jsx("rect",{x:"3.5",y:"3.5",width:"7",height:"7",rx:"1.5",strokeLinejoin:"round"}),n.jsx("rect",{x:"13.5",y:"3.5",width:"7",height:"7",rx:"1.5",strokeLinejoin:"round"}),n.jsx("rect",{x:"3.5",y:"13.5",width:"7",height:"7",rx:"1.5",strokeLinejoin:"round"}),n.jsx("rect",{x:"13.5",y:"13.5",width:"7",height:"7",rx:"1.5",strokeLinejoin:"round"})]})},{to:"/activity",section:"observability",label:"Activity",icon:n.jsx("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:n.jsx("path",{d:"M3 12h4l2.5-6 4 12 2.5-6H21",strokeLinecap:"round",strokeLinejoin:"round"})})},{to:"/usage",section:"observability",label:"Usage",icon:n.jsx("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:n.jsx("path",{d:"M4 20V10M10 20V4M16 20v-7M22 20H2",strokeLinecap:"round",strokeLinejoin:"round"})})},{to:"/providers",section:"catalog",label:"Providers",icon:n.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[n.jsx("rect",{x:"3.5",y:"4.5",width:"17",height:"6",rx:"1.5",strokeLinejoin:"round"}),n.jsx("rect",{x:"3.5",y:"13.5",width:"17",height:"6",rx:"1.5",strokeLinejoin:"round"}),n.jsx("path",{d:"M7 7.5h.01M7 16.5h.01",strokeLinecap:"round",strokeLinejoin:"round"})]})},{to:"/users",section:"access",label:"Users",icon:n.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[n.jsx("circle",{cx:"9",cy:"8",r:"3.2",strokeLinejoin:"round"}),n.jsx("path",{d:"M3.5 19a5.5 5.5 0 0 1 11 0",strokeLinecap:"round",strokeLinejoin:"round"}),n.jsx("path",{d:"M16 5.2a3.2 3.2 0 0 1 0 5.6M17.5 19a5.5 5.5 0 0 0-3-4.9",strokeLinecap:"round",strokeLinejoin:"round"})]})},{to:"/keys",section:"access",label:"API keys",icon:n.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[n.jsx("circle",{cx:"7.5",cy:"15.5",r:"3.5"}),n.jsx("path",{d:"M10 13l7-7M14 5l3 3M16.5 7.5l2-2",strokeLinecap:"round",strokeLinejoin:"round"})]})},{to:"/budgets",section:"access",label:"Budgets",icon:n.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[n.jsx("path",{d:"M3 7.5A1.5 1.5 0 0 1 4.5 6H18a1.5 1.5 0 0 1 1.5 1.5V9",strokeLinejoin:"round"}),n.jsx("rect",{x:"3",y:"7.5",width:"18",height:"12",rx:"1.5",strokeLinejoin:"round"}),n.jsx("path",{d:"M16 13.5h.01",strokeLinecap:"round",strokeLinejoin:"round"}),n.jsx("path",{d:"M21 12v3h-3.5a1.5 1.5 0 0 1 0-3H21z",strokeLinejoin:"round"})]})},{to:"/models",section:"catalog",label:"Models",icon:n.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[n.jsx("path",{d:"M12 3l8 4.5v9L12 21l-8-4.5v-9L12 3z",strokeLinejoin:"round"}),n.jsx("path",{d:"M12 12l8-4.5M12 12v9M12 12L4 7.5",strokeLinejoin:"round"})]})},{to:"/aliases",section:"catalog",label:"Aliases",icon:n.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[n.jsx("path",{d:"M20.6 13.4L13.4 20.6a2 2 0 0 1-2.8 0l-7-7A2 2 0 0 1 3 12.2V5a2 2 0 0 1 2-2h7.2a2 2 0 0 1 1.4.6l7 7a2 2 0 0 1 0 2.8z",strokeLinejoin:"round"}),n.jsx("circle",{cx:"7.5",cy:"7.5",r:"1.5"})]})},{to:"/tools",section:"system",label:"Tools & Guardrails",icon:n.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[n.jsx("path",{d:"M14.7 6.3a4 4 0 0 1 5 5l-8.4 8.4a2 2 0 0 1-2.8 0l-2.2-2.2a2 2 0 0 1 0-2.8z",strokeLinejoin:"round"}),n.jsx("path",{d:"M12 9 5 16",strokeLinecap:"round"})]})},{to:"/settings",section:"system",label:"Settings",icon:n.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[n.jsx("circle",{cx:"12",cy:"12",r:"3"}),n.jsx("path",{d:"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z",strokeLinejoin:"round"})]})}];function $t(){const{logout:e}=re(),t=o.useRef(null),r=o.useRef(null),s=o.useRef(null),[a,i]=o.useState(Ft),[u,d]=o.useState(Ut),[f,y]=o.useState(!1),[h,b]=o.useState(Rt),[m,N]=o.useState(!1);o.useEffect(()=>{if(typeof window>"u"||typeof window.matchMedia!="function")return;const l=window.matchMedia(Pe),v=j=>{b(j.matches),j.matches||N(!1)};return typeof l.addEventListener=="function"?(l.addEventListener("change",v),()=>l.removeEventListener("change",v)):(l.addListener(v),()=>l.removeListener(v))},[]),o.useEffect(()=>{if(!m)return;const l=v=>{v.key==="Escape"&&N(!1)};return window.addEventListener("keydown",l),()=>window.removeEventListener("keydown",l)},[m]),o.useEffect(()=>{var l,v,j;h&&(m?(l=t.current)==null||l.focus():(v=t.current)!=null&&v.contains(document.activeElement)&&((j=s.current)==null||j.focus()))},[h,m]);const M=o.useCallback(l=>{if(l.key!=="Tab")return;const v=Mt(t.current);if(v.length===0)return;const j=v[0],k=v[v.length-1],F=document.activeElement;l.shiftKey&&(F===j||F===t.current)?(l.preventDefault(),k.focus()):!l.shiftKey&&F===k&&(l.preventDefault(),j.focus())},[]);o.useEffect(()=>{const l=window.setTimeout(()=>{try{window.localStorage.setItem(ke,String(Math.round(a)))}catch{}},200);return()=>window.clearTimeout(l)},[a]),o.useEffect(()=>{try{window.localStorage.setItem(Ee,u?"1":"0")}catch{}},[u]);const Ce=o.useCallback(l=>{l.preventDefault(),l.currentTarget.setPointerCapture(l.pointerId),y(!0)},[]),Te=o.useCallback(l=>{var j;if(!l.currentTarget.hasPointerCapture(l.pointerId))return;const v=((j=t.current)==null?void 0:j.getBoundingClientRect().left)??0;i(B(l.clientX-v))},[]),_e=o.useCallback(l=>{l.currentTarget.hasPointerCapture(l.pointerId)&&l.currentTarget.releasePointerCapture(l.pointerId),y(!1)},[]),Le=o.useCallback(l=>{var v;l.preventDefault(),(v=r.current)==null||v.focus()},[]),De=o.useCallback(l=>{l.key==="ArrowLeft"?(l.preventDefault(),i(v=>B(v-he))):l.key==="ArrowRight"&&(l.preventDefault(),i(v=>B(v+he)))},[]),Ae=u?qt:a,S=h?!1:u,H=h&&m?!0:void 0;return n.jsxs("div",{className:T("relative flex h-full flex-col overflow-hidden",f&&"cursor-col-resize select-none"),children:[n.jsx("button",{type:"button",inert:H,onClick:Le,className:"sr-only focus:not-sr-only focus:absolute focus:top-3 focus:left-3 focus:z-50 focus:rounded-lg focus:border focus:border-[var(--otari-brand)] focus:bg-[var(--otari-surface)] focus:px-4 focus:py-2 focus:text-sm focus:font-medium focus:text-[var(--otari-brand-dark)] focus:shadow-md focus:outline-none",children:"Skip to main content"}),n.jsxs("header",{inert:H,className:"flex shrink-0 items-center justify-between border-b border-[var(--otari-line)] bg-[var(--otari-surface)] px-5 py-3",children:[n.jsxs("div",{className:"flex items-center gap-2.5",children:[n.jsx("button",{type:"button",ref:s,onClick:()=>N(l=>!l),"aria-label":m?"Close navigation":"Open navigation","aria-expanded":m,"aria-controls":"app-sidebar",className:"-ml-1 flex h-8 w-8 items-center justify-center rounded-lg text-[var(--otari-muted)] transition-colors hover:bg-[var(--otari-bg)] hover:text-[var(--otari-ink)] md:hidden",children:n.jsx("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5",children:n.jsx("path",{d:"M4 6h16M4 12h16M4 18h16",strokeLinecap:"round",strokeLinejoin:"round"})})}),n.jsx("img",{src:"/favicon.svg",alt:"",className:"h-7 w-7 shrink-0"}),n.jsx("span",{className:"text-base font-semibold text-[var(--otari-ink)]",children:"Otari"})]}),n.jsx(C,{size:"sm",variant:"outline",onPress:e,"aria-label":"Sign out",children:"Sign out"})]}),n.jsx(It,{}),n.jsx(rt,{}),n.jsx(Dt,{}),n.jsxs("div",{className:"flex min-h-0 flex-1",children:[h&&m?n.jsx("div",{"aria-hidden":"true",onClick:()=>N(!1),className:"fixed inset-0 z-30 bg-black/40 md:hidden"}):null,n.jsxs("aside",{ref:t,id:"app-sidebar",role:h?"dialog":void 0,"aria-modal":h&&m?!0:void 0,"aria-label":h?"Navigation":void 0,tabIndex:h?-1:void 0,inert:h&&!m?!0:void 0,onKeyDown:h&&m?M:void 0,style:h?void 0:{width:Ae},className:T("flex flex-col border-r border-[var(--otari-line)] bg-[var(--otari-surface)] focus:outline-none",h?T("fixed inset-y-0 left-0 z-40 w-[17rem] shadow-xl transition-transform duration-200",m?"translate-x-0":"-translate-x-full"):T("relative shrink-0",!f&&"transition-[width] duration-150")),children:[n.jsx("button",{type:"button",onClick:()=>d(l=>!l),"aria-label":u?"Expand sidebar":"Collapse sidebar","aria-pressed":u,title:u?"Expand sidebar":"Collapse sidebar",className:"absolute -right-3 top-4 z-30 hidden h-6 w-6 items-center justify-center rounded-full border border-[var(--otari-line)] bg-[var(--otari-surface)] text-[var(--otari-muted)] shadow-sm transition-colors hover:border-[var(--otari-brand)] hover:text-[var(--otari-brand-dark)] md:flex",children:n.jsx("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",className:T("h-3.5 w-3.5 transition-transform",u&&"rotate-180"),children:n.jsx("path",{d:"M15 6l-6 6 6 6",strokeLinecap:"round",strokeLinejoin:"round"})})}),n.jsx("nav",{className:T("flex flex-col py-4",S?"px-2":"px-3"),children:Kt.map((l,v)=>{const j=Bt.filter(k=>k.section===l.key);return j.length===0?null:n.jsxs("div",{className:v>0?"mt-4":void 0,children:[!S&&l.label?n.jsx("div",{className:"px-3 pb-1 text-[11px] font-semibold tracking-wider text-[var(--otari-muted)] uppercase",children:l.label}):null,v>0&&(S||!l.label)?n.jsx("div",{className:"mx-1 mb-2 border-t border-[var(--otari-line)]"}):null,n.jsx("div",{className:"flex flex-col gap-1",children:j.map(k=>n.jsxs($,{to:k.to,end:k.end,onClick:()=>N(!1),"aria-label":S?k.label:void 0,title:S?k.label:void 0,className:({isActive:F})=>T("flex items-center rounded-lg py-2 text-sm font-medium transition-colors",S?"justify-center px-0":"gap-3 px-3",F?"bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]":"text-[var(--otari-muted)] hover:bg-[var(--otari-bg)] hover:text-[var(--otari-ink)]"),children:[k.icon,S?null:k.label]},k.to))})]},l.key)})}),n.jsxs("div",{className:"mt-auto flex flex-col gap-1 pb-3",children:[n.jsxs($,{to:"/docs",onClick:()=>N(!1),"aria-label":S?"User guide":void 0,title:S?"User guide":void 0,className:({isActive:l})=>T("flex items-center rounded-lg py-2 text-sm font-medium transition-colors",S?"mx-2 justify-center px-0":"mx-3 gap-3 px-3",l?"bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]":"text-[var(--otari-muted)] hover:bg-[var(--otari-bg)] hover:text-[var(--otari-ink)]"),children:[n.jsxs("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[n.jsx("path",{d:"M12 6.5C10.5 5 8 4.5 4 4.5V18c4 0 6.5.5 8 2 1.5-1.5 4-2 8-2V4.5c-4 0-6.5.5-8 2z",strokeLinejoin:"round"}),n.jsx("path",{d:"M12 6.5V20",strokeLinecap:"round"})]}),S?null:"User guide"]}),n.jsxs("a",{href:"https://otari.ai",target:"_blank",rel:"noreferrer",title:"otari.ai: the hosted Otari gateway",className:T("flex items-center rounded-lg py-2 text-xs font-medium text-[var(--otari-muted)] transition-colors hover:bg-[var(--otari-bg)] hover:text-[var(--otari-brand-dark)]",S?"mx-2 justify-center px-0":"mx-3 gap-2 px-3"),children:[n.jsx("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-4 w-4 shrink-0",children:n.jsx("path",{d:"M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z",strokeLinejoin:"round"})}),S?null:n.jsxs("span",{className:"flex-1",children:["otari.ai ",n.jsx("span",{"aria-hidden":!0,children:"↗"})]})]})]}),u||h?null:n.jsx("div",{role:"separator","aria-orientation":"vertical","aria-label":"Resize sidebar","aria-valuenow":Math.round(a),"aria-valuemin":we,"aria-valuemax":Se,tabIndex:0,onPointerDown:Ce,onPointerMove:Te,onPointerUp:_e,onKeyDown:De,className:T("absolute top-0 right-0 z-10 h-full w-1.5 cursor-col-resize touch-none transition-colors","hover:bg-[var(--otari-brand)] focus-visible:bg-[var(--otari-brand)] focus:outline-none",f?"bg-[var(--otari-brand)]":"bg-transparent")})]}),n.jsx("main",{ref:r,id:"main-content",tabIndex:-1,inert:H,className:"flex-1 overflow-y-auto focus:outline-none",children:n.jsx("div",{className:"mx-auto flex max-w-[1800px] flex-col gap-6 px-4 py-5 md:px-6 md:py-6",children:n.jsx(Fe,{})})})]})]})}function zt(){const{login:e}=re(),[t,r]=o.useState(""),[s,a]=o.useState(null),[i,u]=o.useState(!1),d=async()=>{const f=t.trim();if(!(!f||i)){u(!0),a(null);try{await Ye(f)?e():a(new Error("Invalid master key."))}catch(y){a(y)}finally{u(!1)}}};return n.jsx("div",{className:"flex min-h-full items-center justify-center p-6",children:n.jsx(I,{className:"w-full max-w-md",children:n.jsxs(I.Content,{className:"flex flex-col gap-5 p-7",children:[n.jsxs("div",{className:"flex flex-col items-center gap-3 text-center",children:[n.jsx("img",{src:"/favicon.svg",alt:"Otari",className:"h-12 w-12"}),n.jsxs("div",{children:[n.jsx("h1",{className:"text-lg font-semibold text-[var(--otari-ink)]",children:"Otari Dashboard"}),n.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:"Sign in with your master key to browse models, set pricing, and manage settings."})]})]}),n.jsxs("form",{className:"flex flex-col gap-4",onSubmit:f=>{f.preventDefault(),d()},children:[n.jsxs(Ve,{value:t,onChange:f=>{r(f),s&&a(null)},type:"password",isRequired:!0,className:"flex flex-col gap-1",children:[n.jsx(xe,{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Master key"}),n.jsx(ye,{placeholder:"otari-mk-… or your master key",autoFocus:!0,autoComplete:"off"})]}),n.jsxs("details",{className:"text-xs text-[var(--otari-muted)]",children:[n.jsx("summary",{className:"cursor-pointer font-medium text-[var(--otari-brand-dark)]",children:"First run? Where to find your key"}),n.jsxs("p",{className:"mt-2 leading-relaxed",children:["If you did not set ",n.jsx("code",{children:"OTARI_MASTER_KEY"}),", Otari generated one and printed it to the server logs on startup. Look for the line ",n.jsx("code",{children:"Your master key:"})," (for example, run"," ",n.jsx("code",{children:"docker logs "}),") and paste it above."]})]}),n.jsx(Ct,{error:s}),n.jsx(C,{type:"submit",variant:"primary",fullWidth:!0,isDisabled:!t.trim()||i,children:i?"Signing in…":"Sign in"})]}),n.jsx("p",{className:"text-center text-xs text-[var(--otari-muted)]",children:"The key is sent once to this gateway and exchanged for a session cookie; it is never stored in the browser."}),n.jsx("div",{className:"border-t border-[var(--otari-line)] pt-4 text-center",children:n.jsx(We,{href:"/welcome",className:"text-sm font-medium text-[var(--otari-brand-dark)]",children:"New to Otari? Open the welcome guide"})})]})})})}const Qt=o.lazy(async()=>({default:(await P(async()=>{const{ActivityPage:e}=await import("./ActivityPage-D2jsRt9a.js");return{ActivityPage:e}},__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]))).ActivityPage})),Vt=o.lazy(async()=>({default:(await P(async()=>{const{AliasesPage:e}=await import("./AliasesPage-CrabMwlU.js");return{AliasesPage:e}},__vite__mapDeps([10,1,2,6,4,7,8,11]))).AliasesPage})),Wt=o.lazy(async()=>({default:(await P(async()=>{const{BudgetsPage:e}=await import("./BudgetsPage-DjVks5Lb.js");return{BudgetsPage:e}},__vite__mapDeps([12,1,2,6,4,7,8,11]))).BudgetsPage})),Ht=o.lazy(async()=>({default:(await P(async()=>{const{DocsPage:e}=await import("./DocsPage-Bo_X9aMz.js");return{DocsPage:e}},__vite__mapDeps([13,1,2,4]))).DocsPage})),Gt=o.lazy(async()=>({default:(await P(async()=>{const{KeysPage:e}=await import("./KeysPage--hq_RPOL.js");return{KeysPage:e}},__vite__mapDeps([14,1,2,6,4,7,8,11,15]))).KeysPage})),Jt=o.lazy(async()=>({default:(await P(async()=>{const{ModelsPage:e}=await import("./ModelsPage-Dt9ZAYGy.js");return{ModelsPage:e}},__vite__mapDeps([16,1,2,6,4,8,9]))).ModelsPage})),Yt=o.lazy(async()=>({default:(await P(async()=>{const{OverviewIndex:e}=await import("./OverviewPage-DPI1n-nt.js");return{OverviewIndex:e}},__vite__mapDeps([17,1,2,18,5,4,8]))).OverviewIndex})),Xt=o.lazy(async()=>({default:(await P(async()=>{const{ProvidersPage:e}=await import("./ProvidersPage-CC2Glu9P.js");return{ProvidersPage:e}},__vite__mapDeps([19,1,2,11,4,8]))).ProvidersPage})),Zt=o.lazy(async()=>({default:(await P(async()=>{const{SettingsPage:e}=await import("./SettingsPage-CYDMBdEO.js");return{SettingsPage:e}},__vite__mapDeps([20,1,2,4]))).SettingsPage})),en=o.lazy(async()=>({default:(await P(async()=>{const{ToolsGuardrailsPage:e}=await import("./ToolsGuardrailsPage-B68S03Js.js");return{ToolsGuardrailsPage:e}},__vite__mapDeps([21,1,2,4]))).ToolsGuardrailsPage})),tn=o.lazy(async()=>({default:(await P(async()=>{const{UsagePage:e}=await import("./UsagePage-BYqtE3tC.js");return{UsagePage:e}},__vite__mapDeps([22,1,2,3,4,5,6,18,7,8,9]))).UsagePage})),nn=o.lazy(async()=>({default:(await P(async()=>{const{UsersPage:e}=await import("./UsersPage-CZXIBvdD.js");return{UsersPage:e}},__vite__mapDeps([23,1,2,6,4,7,8,11,15]))).UsersPage}));function E(e){return n.jsx(o.Suspense,{fallback:n.jsx("div",{role:"status",children:"Loading page…"}),children:e})}function rn(){const{isAuthenticated:e}=re();return e?n.jsx(Ue,{children:n.jsx(Ke,{children:n.jsxs(w,{element:n.jsx($t,{}),children:[n.jsx(w,{index:!0,element:E(n.jsx(Yt,{}))}),n.jsx(w,{path:"providers",element:E(n.jsx(Xt,{}))}),n.jsx(w,{path:"keys",element:E(n.jsx(Gt,{}))}),n.jsx(w,{path:"users",element:E(n.jsx(nn,{}))}),n.jsx(w,{path:"budgets",element:E(n.jsx(Wt,{}))}),n.jsx(w,{path:"activity",element:E(n.jsx(Qt,{}))}),n.jsx(w,{path:"usage",element:E(n.jsx(tn,{}))}),n.jsx(w,{path:"models",element:E(n.jsx(Jt,{}))}),n.jsx(w,{path:"aliases",element:E(n.jsx(Vt,{}))}),n.jsx(w,{path:"tools",element:E(n.jsx(en,{}))}),n.jsx(w,{path:"settings",element:E(n.jsx(Zt,{}))}),n.jsx(w,{path:"docs",element:E(n.jsx(Ht,{}))}),n.jsx(w,{path:"*",element:n.jsx(Be,{to:"/",replace:!0})})]})})}):n.jsx(zt,{})}function sn({children:e}){const[t]=o.useState(()=>new Re({defaultOptions:{queries:{refetchOnWindowFocus:!1,retry:(r,s)=>s instanceof L&&(s.status===401||s.status===403)?!1:r<2}}}));return n.jsx(Oe,{client:t,children:n.jsx(et,{children:e})})}const Ne=document.getElementById("root");if(!Ne)throw new Error("Root element #root not found");He.createRoot(Ne).render(n.jsx(o.StrictMode,{children:n.jsx(sn,{children:n.jsx(rn,{})})}));export{Et as $,mn as A,Xn as B,hn as C,Gn as D,Ct as E,Nr as F,bn as G,Bn as H,Tt as I,In as J,$n as K,xr as L,zn as M,Nt as N,yr as O,wr as P,dt as Q,Sr as R,hr as S,wn as T,Er as U,En as V,pr as W,br as X,vr as Y,jr as Z,fr as _,Hn as a,Pt as a0,Nn as a1,_n as a2,Dn as a3,mt as a4,Tn as a5,Pn as a6,Cn as a7,kn as a8,Sn as a9,An as aa,Qn as ab,Vn as ac,Wn as ad,Mn as ae,Ln as af,Fn as ag,Un as ah,Kn as ai,dn as aj,un as ak,gr as al,pn as am,it as an,gn as ao,vn as ap,or as aq,ar,lr as b,cr as c,mr as d,yn as e,xn as f,ur as g,dr as h,Cr as i,fn as j,at as k,jn as l,qn as m,On as n,Pr as o,Rn as p,Zn as q,tr as r,nr as s,rr as t,sr as u,ir as v,kr as w,er as x,Jn as y,Yn as z}; diff --git a/src/gateway/static/dashboard/assets/index-DV8qvdUj.js b/src/gateway/static/dashboard/assets/index-DV8qvdUj.js deleted file mode 100644 index b9e0db48..00000000 --- a/src/gateway/static/dashboard/assets/index-DV8qvdUj.js +++ /dev/null @@ -1,2 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ActivityPage-DIACtZv5.js","assets/tanstack-query-1t81HyiD.js","assets/react-dgEcD0HR.js","assets/FilterChips-DZjxf-ot.js","assets/heroui-BI50yK5B.js","assets/recharts-C4kRDmvS.js","assets/tableSelection-9hV37uhu.js","assets/ConfirmDialog-KvA7b1-a.js","assets/DataTable-Bmn_eYSV.js","assets/TablePagination-CXhesdmH.js","assets/AliasesPage-x0NFKgtH.js","assets/Field-Dwp6u3t8.js","assets/BudgetsPage-D-S9-U_l.js","assets/DocsPage-BZcIfS8u.js","assets/KeysPage-CZjqxzex.js","assets/ModelScopeControl-u-XlXgXz.js","assets/ModelsPage-BkzSZx5b.js","assets/OverviewPage-BedTU_aZ.js","assets/charts-DIsd9m--.js","assets/ProvidersPage-BP60GuZv.js","assets/SettingsPage-DR0J2MHH.js","assets/ToolsGuardrailsPage-DiZ1TbOa.js","assets/UsagePage-Ce_jAzFl.js","assets/UsersPage-5kctrw19.js"])))=>i.map(i=>d[i]); -var Le=Object.defineProperty;var Ie=(e,r,n)=>r in e?Le(e,r,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[r]=n;var ie=(e,r,n)=>Ie(e,typeof r!="symbol"?r+"":r,n);import{u as y,j as t,a as g,b as h,k as X,Q as Ae,c as qe}from"./tanstack-query-1t81HyiD.js";import{d as Re,r as a,N as G,O as De,H as Oe,e as Me,f as w,h as Fe}from"./react-dgEcD0HR.js";import{B as C,C as M,L as me,I as fe,a as Ue,b as Ke,d as I,S as Be,c as _,T as ze,e as $e}from"./heroui-BI50yK5B.js";(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))s(i);new MutationObserver(i=>{for(const o of i)if(o.type==="childList")for(const d of o.addedNodes)d.tagName==="LINK"&&d.rel==="modulepreload"&&s(d)}).observe(document,{childList:!0,subtree:!0});function n(i){const o={};return i.integrity&&(o.integrity=i.integrity),i.referrerPolicy&&(o.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?o.credentials="include":i.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function s(i){if(i.ep)return;i.ep=!0;const o=n(i);fetch(i.href,o)}})();var Qe=Re();const Ve="modulepreload",We=function(e){return"/"+e},oe={},P=function(r,n,s){let i=Promise.resolve();if(n&&n.length>0){let d=function(x){return Promise.all(x.map(f=>Promise.resolve(f).then(b=>({status:"fulfilled",value:b}),b=>({status:"rejected",reason:b}))))};document.getElementsByTagName("link");const m=document.querySelector("meta[property=csp-nonce]"),p=(m==null?void 0:m.nonce)||(m==null?void 0:m.getAttribute("nonce"));i=d(n.map(x=>{if(x=We(x),x in oe)return;oe[x]=!0;const f=x.endsWith(".css"),b=f?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${x}"]${b}`))return;const u=document.createElement("link");if(u.rel=f?"stylesheet":Ve,f||(u.as="script"),u.crossOrigin="",u.href=x,p&&u.setAttribute("nonce",p),document.head.appendChild(u),f)return new Promise((N,R)=>{u.addEventListener("load",N),u.addEventListener("error",()=>R(new Error(`Unable to preload CSS for ${x}`)))})}))}function o(d){const m=new Event("vite:preloadError",{cancelable:!0});if(m.payload=d,window.dispatchEvent(m),!m.defaultPrevented)throw d}return i.then(d=>{for(const m of d||[])m.status==="rejected"&&o(m.reason);return r().catch(o)})};class T extends Error{constructor(n,s){super(s);ie(this,"status");this.name="ApiError",this.status=n}}let F=null;function le(e){F=e}async function J(e){try{const r=await e.json();if(typeof r.detail=="string")return r.detail;if(r.detail!=null)return JSON.stringify(r.detail)}catch{}return e.statusText||`Request failed (${e.status})`}async function He(e){let r;try{r=await fetch("/v1/auth/session",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({master_key:e})})}catch{throw new T(0,"Network error: could not reach the gateway.")}if(r.status===401||r.status===403)return!1;if(!r.ok)throw new T(r.status,await J(r));return!0}async function Ge(){try{await fetch("/v1/auth/session",{method:"DELETE"})}catch{}}async function c(e,r={}){const n=new Headers(r.headers);n.set("Accept","application/json"),r.body!=null&&!n.has("Content-Type")&&n.set("Content-Type","application/json");let s;try{s=await fetch(e,{...r,headers:n})}catch{throw new T(0,"Network error: could not reach the gateway.")}if(s.status===401||s.status===403)throw F==null||F(),new T(s.status,await J(s));if(!s.ok)throw new T(s.status,await J(s));if(s.status!==204)return await s.json()}const Y="otari.dashboard.hasSession",he=a.createContext(null);function Je(){try{return window.localStorage.getItem(Y)==="1"}catch{return!1}}function Ye({children:e}){const r=y(),[n,s]=a.useState(Je),i=a.useCallback(()=>{Ge(),s(!1),r.clear();try{window.localStorage.removeItem(Y)}catch{}},[r]),o=a.useCallback(()=>{r.clear(),s(!0);try{window.localStorage.setItem(Y,"1")}catch{}},[r]);a.useEffect(()=>(le(i),()=>le(null)),[i]);const d=a.useMemo(()=>({isAuthenticated:n,login:o,logout:i}),[n,o,i]);return t.jsx(he.Provider,{value:d,children:e})}function Z(){const e=a.useContext(he);if(!e)throw new Error("useAuth must be used within an AuthProvider");return e}function Xe(e){return e instanceof T&&e.status===0}function Ze(){const e=y(),[r,n]=a.useState(!1);return a.useEffect(()=>{const s=e.getQueryCache(),i=()=>s.getAll().some(o=>o.state.status==="error"&&Xe(o.state.error));return n(i()),s.subscribe(()=>n(i()))},[e]),r}function et(){return Ze()?t.jsxs("div",{role:"alert","aria-live":"assertive",className:"fixed right-4 bottom-4 z-50 flex max-w-sm items-start gap-2.5 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 shadow-lg",children:[t.jsxs("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,className:"mt-0.5 h-5 w-5 shrink-0",children:[t.jsx("path",{d:"M12 9v4M12 17h.01",strokeLinecap:"round",strokeLinejoin:"round"}),t.jsx("path",{d:"M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z",strokeLinejoin:"round"})]}),t.jsxs("span",{children:[t.jsx("strong",{className:"font-semibold",children:"Can’t reach the gateway."})," The backend isn’t responding; data won’t load or save until the connection is restored."]})]}):null}const L="models",K="pricing",xe="settings",ve="tool-settings",ee="aliases",te="discoverable",re="providers",ne="provider-health",ye="stored-providers",tt="model-metadata",rt="build",A="keys",q="budgets",pe="users",O="usage",nt=6e4,ce=60*6e4;function tr(){return g({queryKey:[L],queryFn:()=>c("/v1/models"),staleTime:6e4})}function st(){return g({queryKey:[rt],queryFn:()=>c("/dashboard-build.json"),refetchInterval:nt,refetchOnWindowFocus:!0,staleTime:0,retry:!1})}function rr(){return g({queryKey:[te],queryFn:()=>c("/v1/models/discoverable"),staleTime:5*6e4})}function nr(){return g({queryKey:[re],queryFn:()=>c("/v1/providers"),staleTime:5*6e4})}function sr(){return g({queryKey:["provider-catalog"],queryFn:()=>c("/v1/providers/catalog"),staleTime:1/0})}function ar(e){return g({queryKey:["provider-catalog",e],queryFn:()=>c(`/v1/providers/catalog/${encodeURIComponent(e)}`),enabled:e!=="",staleTime:1/0})}function ir(){return g({queryKey:[ne],queryFn:()=>c("/v1/providers/health"),staleTime:ce,refetchInterval:ce})}function or(){const e=y();return h({mutationFn:()=>c("/v1/providers/health?refresh=true"),onSuccess:r=>e.setQueryData([ne],r)})}function lr(){return g({queryKey:[ye],queryFn:()=>c("/v1/provider-credentials"),staleTime:6e4})}function B(e){e.invalidateQueries({queryKey:[ye]}),e.invalidateQueries({queryKey:[re]}),e.invalidateQueries({queryKey:[L]}),e.invalidateQueries({queryKey:[te]}),e.invalidateQueries({queryKey:[ne]})}function cr(){const e=y();return h({mutationFn:r=>c("/v1/provider-credentials",{method:"POST",body:JSON.stringify(r)}),onSuccess:()=>B(e)})}function ur(){const e=y();return h({mutationFn:({instance:r,body:n})=>c(`/v1/provider-credentials/${encodeURIComponent(r)}`,{method:"PATCH",body:JSON.stringify(n)}),onSuccess:()=>B(e)})}function dr(){const e=y();return h({mutationFn:r=>c(`/v1/provider-credentials/${encodeURIComponent(r)}`,{method:"DELETE"}),onSuccess:()=>B(e)})}function mr(){const e=y();return h({mutationFn:()=>c("/v1/provider-credentials/reencrypt",{method:"POST"}),onSuccess:()=>B(e)})}function fr(){return h({mutationFn:e=>c(`/v1/provider-credentials/${encodeURIComponent(e)}/test`,{method:"POST"})})}function hr(){return h({mutationFn:e=>c("/v1/provider-credentials/test",{method:"POST",body:JSON.stringify(e)})})}function xr(){return g({queryKey:[tt],queryFn:()=>c("/v1/models/metadata"),staleTime:10*6e4})}function vr(){return g({queryKey:[ee],queryFn:()=>c("/v1/aliases"),staleTime:6e4})}function yr(){const e=y();return h({mutationFn:r=>c("/v1/aliases",{method:"POST",body:JSON.stringify(r)}),onSuccess:()=>{e.invalidateQueries({queryKey:[ee]}),e.invalidateQueries({queryKey:[L]})}})}function pr(){const e=y();return h({mutationFn:r=>c(`/v1/aliases/${encodeURIComponent(r)}`,{method:"DELETE"}),onSuccess:()=>{e.invalidateQueries({queryKey:[ee]}),e.invalidateQueries({queryKey:[L]})}})}function at(){return g({queryKey:[xe],queryFn:()=>c("/v1/settings"),staleTime:6e4})}function it(){const e=y();return h({mutationFn:r=>c("/v1/settings",{method:"PATCH",body:JSON.stringify(r)}),onSuccess:r=>{e.setQueryData([xe],r),e.invalidateQueries({queryKey:[L]}),e.invalidateQueries({queryKey:[te]})}})}function gr(){return h({mutationFn:()=>c("/v1/settings/master-key/rotate",{method:"POST"})})}function br(){return g({queryKey:[ve],queryFn:()=>c("/v1/tool-settings"),staleTime:6e4})}function jr(){const e=y();return h({mutationFn:r=>c("/v1/tool-settings",{method:"PATCH",body:JSON.stringify(r)}),onSuccess:r=>{e.setQueryData([ve],r)}})}function wr(){return h({mutationFn:({service:e,url:r})=>c(`/v1/tool-settings/${encodeURIComponent(e)}/test`,{method:"POST",body:JSON.stringify({url:r})})})}const $=1e3,ot=100;async function lt(){const e=[];for(let r=0;rc("/v1/pricing",{method:"POST",body:JSON.stringify(r)}),onSuccess:()=>{e.invalidateQueries({queryKey:[K]}),e.invalidateQueries({queryKey:[L]})}})}function Er(){const e=y();return h({mutationFn:r=>c(`/v1/pricing/${encodeURIComponent(r)}`,{method:"DELETE"}),onSuccess:()=>{e.invalidateQueries({queryKey:[K]}),e.invalidateQueries({queryKey:[L]})}})}function Pr(){return h({mutationFn:()=>c("/v1/pricing/refresh",{method:"POST"})})}function Nr(){const e=y();return h({mutationFn:()=>c("/v1/pricing/refresh/confirm",{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:[K]}),e.invalidateQueries({queryKey:[L]}),e.invalidateQueries({queryKey:[re]})}})}function Cr(){return h({mutationFn:()=>c("/v1/pricing/refresh/reject",{method:"POST"})})}const Q=1e3,ct=100;async function ut(){const e=[];for(let r=0;rc("/v1/keys",{method:"POST",body:JSON.stringify(r)}),onSuccess:()=>void e.invalidateQueries({queryKey:[A]})})}function Lr(){const e=y();return h({mutationFn:({id:r,body:n})=>c(`/v1/keys/${encodeURIComponent(r)}`,{method:"PATCH",body:JSON.stringify(n)}),onSuccess:()=>void e.invalidateQueries({queryKey:[A]})})}function Ir(){const e=y();return h({mutationFn:r=>c(`/v1/keys/${encodeURIComponent(r)}/rotate`,{method:"POST"}),onSuccess:()=>void e.invalidateQueries({queryKey:[A]})})}function Ar(){const e=y();return h({mutationFn:r=>c(`/v1/keys/${encodeURIComponent(r)}`,{method:"DELETE"}),onSuccess:()=>void e.invalidateQueries({queryKey:[A]})})}const V=1e3,dt=100;async function mt(){const e=[];for(let r=0;rc(`/v1/budgets/${encodeURIComponent(e)}/reset-logs`),enabled:e!==null,staleTime:6e4})}function Dr(){const e=y();return h({mutationFn:r=>c("/v1/budgets",{method:"POST",body:JSON.stringify(r)}),onSuccess:()=>void e.invalidateQueries({queryKey:[q]})})}function Or(){const e=y();return h({mutationFn:({id:r,body:n})=>c(`/v1/budgets/${encodeURIComponent(r)}`,{method:"PATCH",body:JSON.stringify(n)}),onSuccess:()=>void e.invalidateQueries({queryKey:[q]})})}function Mr(){const e=y();return h({mutationFn:r=>c(`/v1/budgets/${encodeURIComponent(r)}`,{method:"DELETE"}),onSuccess:()=>void e.invalidateQueries({queryKey:[q]})})}const W=1e3,ft=100;async function ht(){const e=[];for(let r=0;rc("/v1/users",{method:"POST",body:JSON.stringify(r)}),onSuccess:()=>se(e)})}function Kr(){const e=y();return h({mutationFn:({id:r,body:n})=>c(`/v1/users/${encodeURIComponent(r)}`,{method:"PATCH",body:JSON.stringify(n)}),onSuccess:()=>se(e)})}function Br(){const e=y();return h({mutationFn:r=>c(`/v1/users/${encodeURIComponent(r)}`,{method:"DELETE"}),onSuccess:()=>{se(e),e.invalidateQueries({queryKey:[A]})}})}function ae(e){const r=new URLSearchParams;return e.start_date&&r.set("start_date",e.start_date),e.end_date&&r.set("end_date",e.end_date),e.status&&r.set("status",e.status),e.model&&r.set("model",e.model),e.endpoint&&r.set("endpoint",e.endpoint),e.user_id&&r.set("user_id",e.user_id),e.api_key_id&&r.set("api_key_id",e.api_key_id),e.source&&r.set("source",e.source),e.priced!==void 0&&r.set("priced",String(e.priced)),e.counts_toward_budget!==void 0&&r.set("counts_toward_budget",String(e.counts_toward_budget)),r}function zr(e,r,n){return g({queryKey:[O,"list",e,r,n],queryFn:()=>{const s=ae(e);return s.set("skip",String(r*n)),s.set("limit",String(n)),c(`/v1/usage?${s.toString()}`)},placeholderData:X,staleTime:1e4})}function $r(e,r=!0){return g({queryKey:[O,"count",e],queryFn:()=>c(`/v1/usage/count?${ae(e).toString()}`),enabled:r,placeholderData:X,staleTime:1e4})}function Qr(){const e=y();return h({mutationFn:r=>c("/v1/usage",{method:"DELETE",body:JSON.stringify(r)}),onSuccess:()=>{e.invalidateQueries({queryKey:[O]})}})}function Vr(){const e=y();return h({mutationFn:r=>c("/v1/usage/set-price",{method:"POST",body:JSON.stringify(r)}),onSuccess:()=>{e.invalidateQueries({queryKey:[O]})}})}function Wr(e,r,n=!0){return g({queryKey:[O,"summary",e,r],queryFn:()=>{const s=ae(e);return s.set("bucket",r),c(`/v1/usage/summary?${s.toString()}`)},enabled:n,placeholderData:X,staleTime:3e4})}function Hr(e){return e==null?"0":new Intl.NumberFormat("en-US").format(e)}function Gr(e){if(e==null)return"$0.00";const r=e!==0&&Math.abs(e)<.01?4:2;return new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",minimumFractionDigits:2,maximumFractionDigits:r}).format(e)}function Jr(e){if(e==null)return"—";if(e>=1e6){const r=e/1e6;return`${Number.isInteger(r)?r:r.toFixed(1)}M`}if(e>=1e3){const r=Math.round(e/1e3);return r>=1e3?"1M":`${r}K`}return String(e)}const xt=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function Yr(e){if(!e)return"—";const r=/^(\d{4})-(\d{2})/.exec(e);if(!r)return e;const n=Number(r[2])-1;return n<0||n>11?r[1]:`${xt[n]} ${r[1]}`}const vt=new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",maximumFractionDigits:2});function Xr(e){return vt.format(e)}function Zr(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function yt(e){return`${(e*100).toFixed(1)}%`}function en(e,r){return r===void 0||r===0?null:(e-r)/r}function pt(e,r=Date.now()){if(!e)return"never";const n=new Date(e);if(Number.isNaN(n.getTime()))return e;const s=Math.round((r-n.getTime())/1e3),i=s<0,o=Math.abs(s),d=[["second",60],["minute",60],["hour",24],["day",30],["month",12],["year",Number.POSITIVE_INFINITY]];let m=o,p="second";for(const[f,b]of d){if(p=f,m0?"▲":e<0?"▼":"•";return t.jsxs("span",{className:"text-[var(--otari-muted)]",children:[r," ",yt(Math.abs(e))," vs prev"]})}function gt(e){return e instanceof T||e instanceof Error?e.message:"Something went wrong."}function bt({error:e}){return e?t.jsx("div",{role:"alert",className:"rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700",children:gt(e)}):null}function jt({tone:e="info",children:r}){const n=e==="warning"?"border-amber-200 bg-amber-50 text-amber-800":"border-[var(--otari-brand)] bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]";return t.jsx("div",{className:`rounded-lg border px-4 py-3 text-sm ${n}`,children:r})}function nn({title:e,description:r,action:n}){return t.jsxs("div",{className:"flex flex-col gap-3",children:[t.jsxs("div",{children:[t.jsx("h1",{className:"text-xl font-semibold text-[var(--otari-ink)]",children:e}),r?t.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:r}):null]}),n?t.jsx("div",{className:"flex flex-wrap gap-2",children:n}):null]})}function wt(e){const[r,n]=a.useState(()=>Date.now());return a.useEffect(()=>{let s;const i=()=>{s===void 0&&(s=setInterval(()=>n(Date.now()),e))},o=()=>{s!==void 0&&(clearInterval(s),s=void 0)},d=()=>{n(Date.now()),document.visibilityState==="visible"?i():o()};return d(),document.addEventListener("visibilitychange",d),()=>{o(),document.removeEventListener("visibilitychange",d)}},[e]),r}function sn({onRefresh:e,isFetching:r=!1,updatedAt:n,label:s="Refresh"}){const i=wt(15e3),o=n?pt(new Date(n).toISOString(),i):null;return t.jsxs("span",{className:"inline-flex items-center gap-2",children:[o?t.jsxs("span",{className:"text-xs text-[var(--otari-muted)]",children:["Updated ",o]}):null,t.jsx(C,{variant:"outline",size:"sm",isIconOnly:!0,isDisabled:r,onPress:e,"aria-label":s,children:t.jsxs("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:`h-4 w-4 ${r?"animate-spin":""}`,"aria-hidden":"true",children:[t.jsx("path",{d:"M20 11a8 8 0 1 0-.5 4",strokeLinecap:"round",strokeLinejoin:"round"}),t.jsx("path",{d:"M20 4v5h-5",strokeLinecap:"round",strokeLinejoin:"round"})]})})]})}function an({title:e,description:r,actionLabel:n,onAction:s,isActionDisabled:i,children:o}){return t.jsx(I,{children:t.jsxs(I.Content,{className:"flex flex-col gap-4 p-6",children:[t.jsxs("div",{children:[t.jsx("h2",{className:"text-lg font-semibold text-[var(--otari-ink)]",children:e}),r?t.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:r}):null]}),o,n&&s?t.jsx("div",{children:t.jsx(C,{variant:"primary",isDisabled:i,onPress:s,children:n})}):null]})})}function on({label:e="Loading…"}){return t.jsxs("div",{role:"status",className:"flex items-center justify-center gap-2 px-4 py-10 text-sm text-[var(--otari-muted)]",children:[t.jsx(Be,{size:"sm"}),t.jsx("span",{children:e})]})}function ln({children:e,confirmLabel:r,onConfirm:n,isPending:s}){const[i,o]=a.useState(!1);return i?t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx(C,{size:"sm",variant:"danger",isDisabled:s,onPress:n,children:r}),t.jsx(C,{size:"sm",variant:"ghost",isDisabled:s,onPress:()=>o(!1),children:"Cancel"})]}):t.jsx(C,{size:"sm",variant:"danger-soft",onPress:()=>o(!0),children:e})}const St="rounded-lg border border-[var(--otari-line)] bg-[var(--otari-bg)] px-3 py-2 text-sm text-[var(--otari-ink)] focus:border-[var(--otari-brand)] focus:outline-none";function cn({id:e,label:r,ariaLabel:n,value:s,onChange:i,options:o,children:d,disabled:m}){const p=a.useId(),x=e??(r?p:void 0),f=t.jsx("select",{id:x,"aria-label":r?void 0:n,value:s,disabled:m,onChange:b=>i(b.target.value),className:St,children:o?o.map(b=>t.jsx("option",{value:b.value,children:b.label},b.value)):d});return r?t.jsxs("div",{className:"flex flex-col gap-1",children:[t.jsx("label",{htmlFor:x,className:"text-xs font-medium text-[var(--otari-muted)]",children:r}),f]}):f}function un({label:e,value:r,onChange:n,options:s,placeholder:i,maxVisible:o=50,allowsCustom:d=!1}){const m=u=>{var N;return((N=s.find(R=>R.value===u))==null?void 0:N.label)??u},[p,x]=a.useState(()=>m(r));a.useEffect(()=>{x(m(r))},[r]);const f=p.trim().toLowerCase(),b=s.filter(u=>!f||u.value.toLowerCase().includes(f)||u.label.toLowerCase().includes(f)).slice(0,o);return t.jsxs(M.Root,{allowsEmptyCollection:!0,allowsCustomValue:d,menuTrigger:"focus",inputValue:p,onInputChange:u=>{x(u),d?n(u.trim()):u.trim()===""&&n("")},onSelectionChange:u=>{u!=null&&n(String(u))},className:"flex flex-col gap-1",children:[t.jsx(me,{className:"text-xs font-medium text-[var(--otari-muted)]",children:e}),t.jsxs(M.InputGroup,{children:[t.jsx(fe,{placeholder:i,autoComplete:"off",onFocus:u=>u.currentTarget.select()}),t.jsx(M.Trigger,{})]}),t.jsx(M.Popover,{children:t.jsx(Ue,{items:b,className:"max-h-72 overflow-auto",children:u=>t.jsx(Ke,{id:u.value,textValue:u.label,children:u.label})})})]})}function kt(){var o;const e=at(),r=it(),[n,s]=a.useState(!1);return!(((o=e.data)==null?void 0:o.require_pricing)===!0&&e.data.default_pricing===!1)||n?null:t.jsx("div",{className:"shrink-0 px-6 pt-3",children:t.jsx(jt,{tone:"warning",children:t.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[t.jsxs("span",{children:["Requests are rejected until pricing is set (",t.jsx("code",{children:"require_pricing"})," is on). Enable default pricing to meter new models with public rates right away."]}),t.jsxs("span",{className:"flex items-center gap-2",children:[t.jsx(C,{size:"sm",variant:"primary",isDisabled:r.isPending,onPress:()=>r.mutate({default_pricing:!0}),children:r.isPending?"Enabling…":"Enable default pricing"}),t.jsx(C,{size:"sm",variant:"ghost",onPress:()=>s(!0),children:"Dismiss"})]})]})})})}function Et(){const{data:e}=st(),r=a.useRef(null);return e&&r.current===null&&(r.current=e.build),e!=null&&r.current!=null&&e.build!==r.current}function Pt(){const e=Et(),[r,n]=a.useState(!1);return!e||r?null:t.jsx("div",{className:"pointer-events-none absolute inset-x-0 top-0 z-50 flex justify-center",children:t.jsxs("div",{role:"status",className:"pointer-events-auto mt-1.5 flex items-center gap-3 rounded-full border border-[var(--otari-brand)] bg-[var(--otari-brand-tint)] py-1.5 pr-1.5 pl-4 text-sm text-[var(--otari-brand-dark)] shadow-md",children:[t.jsxs("span",{children:[t.jsx("strong",{className:"font-semibold",children:"An update is available."})," Reloading keeps you signed in."]}),t.jsx(C,{size:"sm",variant:"primary",onPress:()=>window.location.reload(),children:"Update now"}),t.jsx(C,{size:"sm",variant:"ghost",onPress:()=>n(!0),children:"Later"})]})})}const ge=200,be=480,H=240,Nt=60,je="otari.dashboard.sidebarWidth",we="otari.dashboard.sidebarCollapsed",de=16,Se="(max-width: 767px)",U=e=>Math.min(be,Math.max(ge,e));function Ct(){return typeof window>"u"||typeof window.matchMedia!="function"?!1:window.matchMedia(Se).matches}const _t='a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])';function Tt(e){return e?Array.from(e.querySelectorAll(_t)).filter(r=>r.offsetParent!==null||r===document.activeElement):[]}function Lt(){if(typeof window>"u")return H;try{const e=window.localStorage.getItem(je),r=e?Number.parseInt(e,10):Number.NaN;return Number.isNaN(r)?H:U(r)}catch{return H}}function It(){if(typeof window>"u")return!1;try{return window.localStorage.getItem(we)==="1"}catch{return!1}}const At=[{key:"home"},{key:"observability",label:"Observability"},{key:"catalog",label:"Catalog"},{key:"access",label:"Access"},{key:"system"}],qt=[{to:"/",section:"home",label:"Overview",end:!0,icon:t.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[t.jsx("rect",{x:"3.5",y:"3.5",width:"7",height:"7",rx:"1.5",strokeLinejoin:"round"}),t.jsx("rect",{x:"13.5",y:"3.5",width:"7",height:"7",rx:"1.5",strokeLinejoin:"round"}),t.jsx("rect",{x:"3.5",y:"13.5",width:"7",height:"7",rx:"1.5",strokeLinejoin:"round"}),t.jsx("rect",{x:"13.5",y:"13.5",width:"7",height:"7",rx:"1.5",strokeLinejoin:"round"})]})},{to:"/activity",section:"observability",label:"Activity",icon:t.jsx("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:t.jsx("path",{d:"M3 12h4l2.5-6 4 12 2.5-6H21",strokeLinecap:"round",strokeLinejoin:"round"})})},{to:"/usage",section:"observability",label:"Usage",icon:t.jsx("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:t.jsx("path",{d:"M4 20V10M10 20V4M16 20v-7M22 20H2",strokeLinecap:"round",strokeLinejoin:"round"})})},{to:"/providers",section:"catalog",label:"Providers",icon:t.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[t.jsx("rect",{x:"3.5",y:"4.5",width:"17",height:"6",rx:"1.5",strokeLinejoin:"round"}),t.jsx("rect",{x:"3.5",y:"13.5",width:"17",height:"6",rx:"1.5",strokeLinejoin:"round"}),t.jsx("path",{d:"M7 7.5h.01M7 16.5h.01",strokeLinecap:"round",strokeLinejoin:"round"})]})},{to:"/users",section:"access",label:"Users",icon:t.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[t.jsx("circle",{cx:"9",cy:"8",r:"3.2",strokeLinejoin:"round"}),t.jsx("path",{d:"M3.5 19a5.5 5.5 0 0 1 11 0",strokeLinecap:"round",strokeLinejoin:"round"}),t.jsx("path",{d:"M16 5.2a3.2 3.2 0 0 1 0 5.6M17.5 19a5.5 5.5 0 0 0-3-4.9",strokeLinecap:"round",strokeLinejoin:"round"})]})},{to:"/keys",section:"access",label:"API keys",icon:t.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[t.jsx("circle",{cx:"7.5",cy:"15.5",r:"3.5"}),t.jsx("path",{d:"M10 13l7-7M14 5l3 3M16.5 7.5l2-2",strokeLinecap:"round",strokeLinejoin:"round"})]})},{to:"/budgets",section:"access",label:"Budgets",icon:t.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[t.jsx("path",{d:"M3 7.5A1.5 1.5 0 0 1 4.5 6H18a1.5 1.5 0 0 1 1.5 1.5V9",strokeLinejoin:"round"}),t.jsx("rect",{x:"3",y:"7.5",width:"18",height:"12",rx:"1.5",strokeLinejoin:"round"}),t.jsx("path",{d:"M16 13.5h.01",strokeLinecap:"round",strokeLinejoin:"round"}),t.jsx("path",{d:"M21 12v3h-3.5a1.5 1.5 0 0 1 0-3H21z",strokeLinejoin:"round"})]})},{to:"/models",section:"catalog",label:"Models",icon:t.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[t.jsx("path",{d:"M12 3l8 4.5v9L12 21l-8-4.5v-9L12 3z",strokeLinejoin:"round"}),t.jsx("path",{d:"M12 12l8-4.5M12 12v9M12 12L4 7.5",strokeLinejoin:"round"})]})},{to:"/aliases",section:"catalog",label:"Aliases",icon:t.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[t.jsx("path",{d:"M20.6 13.4L13.4 20.6a2 2 0 0 1-2.8 0l-7-7A2 2 0 0 1 3 12.2V5a2 2 0 0 1 2-2h7.2a2 2 0 0 1 1.4.6l7 7a2 2 0 0 1 0 2.8z",strokeLinejoin:"round"}),t.jsx("circle",{cx:"7.5",cy:"7.5",r:"1.5"})]})},{to:"/tools",section:"system",label:"Tools & Guardrails",icon:t.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[t.jsx("path",{d:"M14.7 6.3a4 4 0 0 1 5 5l-8.4 8.4a2 2 0 0 1-2.8 0l-2.2-2.2a2 2 0 0 1 0-2.8z",strokeLinejoin:"round"}),t.jsx("path",{d:"M12 9 5 16",strokeLinecap:"round"})]})},{to:"/settings",section:"system",label:"Settings",icon:t.jsxs("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[t.jsx("circle",{cx:"12",cy:"12",r:"3"}),t.jsx("path",{d:"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z",strokeLinejoin:"round"})]})}];function Rt(){const{logout:e}=Z(),r=a.useRef(null),n=a.useRef(null),s=a.useRef(null),[i,o]=a.useState(Lt),[d,m]=a.useState(It),[p,x]=a.useState(!1),[f,b]=a.useState(Ct),[u,N]=a.useState(!1);a.useEffect(()=>{if(typeof window>"u"||typeof window.matchMedia!="function")return;const l=window.matchMedia(Se),v=j=>{b(j.matches),j.matches||N(!1)};return typeof l.addEventListener=="function"?(l.addEventListener("change",v),()=>l.removeEventListener("change",v)):(l.addListener(v),()=>l.removeListener(v))},[]),a.useEffect(()=>{if(!u)return;const l=v=>{v.key==="Escape"&&N(!1)};return window.addEventListener("keydown",l),()=>window.removeEventListener("keydown",l)},[u]),a.useEffect(()=>{var l,v,j;f&&(u?(l=r.current)==null||l.focus():(v=r.current)!=null&&v.contains(document.activeElement)&&((j=s.current)==null||j.focus()))},[f,u]);const R=a.useCallback(l=>{if(l.key!=="Tab")return;const v=Tt(r.current);if(v.length===0)return;const j=v[0],k=v[v.length-1],D=document.activeElement;l.shiftKey&&(D===j||D===r.current)?(l.preventDefault(),k.focus()):!l.shiftKey&&D===k&&(l.preventDefault(),j.focus())},[]);a.useEffect(()=>{const l=window.setTimeout(()=>{try{window.localStorage.setItem(je,String(Math.round(i)))}catch{}},200);return()=>window.clearTimeout(l)},[i]),a.useEffect(()=>{try{window.localStorage.setItem(we,d?"1":"0")}catch{}},[d]);const Ee=a.useCallback(l=>{l.preventDefault(),l.currentTarget.setPointerCapture(l.pointerId),x(!0)},[]),Pe=a.useCallback(l=>{var j;if(!l.currentTarget.hasPointerCapture(l.pointerId))return;const v=((j=r.current)==null?void 0:j.getBoundingClientRect().left)??0;o(U(l.clientX-v))},[]),Ne=a.useCallback(l=>{l.currentTarget.hasPointerCapture(l.pointerId)&&l.currentTarget.releasePointerCapture(l.pointerId),x(!1)},[]),Ce=a.useCallback(l=>{var v;l.preventDefault(),(v=n.current)==null||v.focus()},[]),_e=a.useCallback(l=>{l.key==="ArrowLeft"?(l.preventDefault(),o(v=>U(v-de))):l.key==="ArrowRight"&&(l.preventDefault(),o(v=>U(v+de)))},[]),Te=d?Nt:i,S=f?!1:d,z=f&&u?!0:void 0;return t.jsxs("div",{className:_("relative flex h-full flex-col overflow-hidden",p&&"cursor-col-resize select-none"),children:[t.jsx("button",{type:"button",inert:z,onClick:Ce,className:"sr-only focus:not-sr-only focus:absolute focus:top-3 focus:left-3 focus:z-50 focus:rounded-lg focus:border focus:border-[var(--otari-brand)] focus:bg-[var(--otari-surface)] focus:px-4 focus:py-2 focus:text-sm focus:font-medium focus:text-[var(--otari-brand-dark)] focus:shadow-md focus:outline-none",children:"Skip to main content"}),t.jsxs("header",{inert:z,className:"flex shrink-0 items-center justify-between border-b border-[var(--otari-line)] bg-[var(--otari-surface)] px-5 py-3",children:[t.jsxs("div",{className:"flex items-center gap-2.5",children:[t.jsx("button",{type:"button",ref:s,onClick:()=>N(l=>!l),"aria-label":u?"Close navigation":"Open navigation","aria-expanded":u,"aria-controls":"app-sidebar",className:"-ml-1 flex h-8 w-8 items-center justify-center rounded-lg text-[var(--otari-muted)] transition-colors hover:bg-[var(--otari-bg)] hover:text-[var(--otari-ink)] md:hidden",children:t.jsx("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5",children:t.jsx("path",{d:"M4 6h16M4 12h16M4 18h16",strokeLinecap:"round",strokeLinejoin:"round"})})}),t.jsx("img",{src:"/favicon.svg",alt:"",className:"h-7 w-7 shrink-0"}),t.jsx("span",{className:"text-base font-semibold text-[var(--otari-ink)]",children:"Otari"})]}),t.jsx(C,{size:"sm",variant:"outline",onPress:e,"aria-label":"Sign out",children:"Sign out"})]}),t.jsx(Pt,{}),t.jsx(et,{}),t.jsx(kt,{}),t.jsxs("div",{className:"flex min-h-0 flex-1",children:[f&&u?t.jsx("div",{"aria-hidden":"true",onClick:()=>N(!1),className:"fixed inset-0 z-30 bg-black/40 md:hidden"}):null,t.jsxs("aside",{ref:r,id:"app-sidebar",role:f?"dialog":void 0,"aria-modal":f&&u?!0:void 0,"aria-label":f?"Navigation":void 0,tabIndex:f?-1:void 0,inert:f&&!u?!0:void 0,onKeyDown:f&&u?R:void 0,style:f?void 0:{width:Te},className:_("flex flex-col border-r border-[var(--otari-line)] bg-[var(--otari-surface)] focus:outline-none",f?_("fixed inset-y-0 left-0 z-40 w-[17rem] shadow-xl transition-transform duration-200",u?"translate-x-0":"-translate-x-full"):_("relative shrink-0",!p&&"transition-[width] duration-150")),children:[t.jsx("button",{type:"button",onClick:()=>m(l=>!l),"aria-label":d?"Expand sidebar":"Collapse sidebar","aria-pressed":d,title:d?"Expand sidebar":"Collapse sidebar",className:"absolute -right-3 top-4 z-30 hidden h-6 w-6 items-center justify-center rounded-full border border-[var(--otari-line)] bg-[var(--otari-surface)] text-[var(--otari-muted)] shadow-sm transition-colors hover:border-[var(--otari-brand)] hover:text-[var(--otari-brand-dark)] md:flex",children:t.jsx("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",className:_("h-3.5 w-3.5 transition-transform",d&&"rotate-180"),children:t.jsx("path",{d:"M15 6l-6 6 6 6",strokeLinecap:"round",strokeLinejoin:"round"})})}),t.jsx("nav",{className:_("flex flex-col py-4",S?"px-2":"px-3"),children:At.map((l,v)=>{const j=qt.filter(k=>k.section===l.key);return j.length===0?null:t.jsxs("div",{className:v>0?"mt-4":void 0,children:[!S&&l.label?t.jsx("div",{className:"px-3 pb-1 text-[11px] font-semibold tracking-wider text-[var(--otari-muted)] uppercase",children:l.label}):null,v>0&&(S||!l.label)?t.jsx("div",{className:"mx-1 mb-2 border-t border-[var(--otari-line)]"}):null,t.jsx("div",{className:"flex flex-col gap-1",children:j.map(k=>t.jsxs(G,{to:k.to,end:k.end,onClick:()=>N(!1),"aria-label":S?k.label:void 0,title:S?k.label:void 0,className:({isActive:D})=>_("flex items-center rounded-lg py-2 text-sm font-medium transition-colors",S?"justify-center px-0":"gap-3 px-3",D?"bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]":"text-[var(--otari-muted)] hover:bg-[var(--otari-bg)] hover:text-[var(--otari-ink)]"),children:[k.icon,S?null:k.label]},k.to))})]},l.key)})}),t.jsxs("div",{className:"mt-auto flex flex-col gap-1 pb-3",children:[t.jsxs(G,{to:"/docs",onClick:()=>N(!1),"aria-label":S?"User guide":void 0,title:S?"User guide":void 0,className:({isActive:l})=>_("flex items-center rounded-lg py-2 text-sm font-medium transition-colors",S?"mx-2 justify-center px-0":"mx-3 gap-3 px-3",l?"bg-[var(--otari-brand-tint)] text-[var(--otari-brand-dark)]":"text-[var(--otari-muted)] hover:bg-[var(--otari-bg)] hover:text-[var(--otari-ink)]"),children:[t.jsxs("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-5 w-5 shrink-0",children:[t.jsx("path",{d:"M12 6.5C10.5 5 8 4.5 4 4.5V18c4 0 6.5.5 8 2 1.5-1.5 4-2 8-2V4.5c-4 0-6.5.5-8 2z",strokeLinejoin:"round"}),t.jsx("path",{d:"M12 6.5V20",strokeLinecap:"round"})]}),S?null:"User guide"]}),t.jsxs("a",{href:"https://otari.ai",target:"_blank",rel:"noreferrer",title:"otari.ai: the hosted Otari gateway",className:_("flex items-center rounded-lg py-2 text-xs font-medium text-[var(--otari-muted)] transition-colors hover:bg-[var(--otari-bg)] hover:text-[var(--otari-brand-dark)]",S?"mx-2 justify-center px-0":"mx-3 gap-2 px-3"),children:[t.jsx("svg",{"aria-hidden":!0,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"h-4 w-4 shrink-0",children:t.jsx("path",{d:"M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z",strokeLinejoin:"round"})}),S?null:t.jsxs("span",{className:"flex-1",children:["otari.ai ",t.jsx("span",{"aria-hidden":!0,children:"↗"})]})]})]}),d||f?null:t.jsx("div",{role:"separator","aria-orientation":"vertical","aria-label":"Resize sidebar","aria-valuenow":Math.round(i),"aria-valuemin":ge,"aria-valuemax":be,tabIndex:0,onPointerDown:Ee,onPointerMove:Pe,onPointerUp:Ne,onKeyDown:_e,className:_("absolute top-0 right-0 z-10 h-full w-1.5 cursor-col-resize touch-none transition-colors","hover:bg-[var(--otari-brand)] focus-visible:bg-[var(--otari-brand)] focus:outline-none",p?"bg-[var(--otari-brand)]":"bg-transparent")})]}),t.jsx("main",{ref:n,id:"main-content",tabIndex:-1,inert:z,className:"flex-1 overflow-y-auto focus:outline-none",children:t.jsx("div",{className:"mx-auto flex max-w-[1800px] flex-col gap-6 px-4 py-5 md:px-6 md:py-6",children:t.jsx(De,{})})})]})]})}function Dt(){const{login:e}=Z(),[r,n]=a.useState(""),[s,i]=a.useState(null),[o,d]=a.useState(!1),m=async()=>{const p=r.trim();if(!(!p||o)){d(!0),i(null);try{await He(p)?e():i(new Error("Invalid master key."))}catch(x){i(x)}finally{d(!1)}}};return t.jsx("div",{className:"flex min-h-full items-center justify-center p-6",children:t.jsx(I,{className:"w-full max-w-md",children:t.jsxs(I.Content,{className:"flex flex-col gap-5 p-7",children:[t.jsxs("div",{className:"flex flex-col items-center gap-3 text-center",children:[t.jsx("img",{src:"/favicon.svg",alt:"Otari",className:"h-12 w-12"}),t.jsxs("div",{children:[t.jsx("h1",{className:"text-lg font-semibold text-[var(--otari-ink)]",children:"Otari Dashboard"}),t.jsx("p",{className:"mt-1 text-sm text-[var(--otari-muted)]",children:"Sign in with your master key to browse models, set pricing, and manage settings."})]})]}),t.jsxs("form",{className:"flex flex-col gap-4",onSubmit:p=>{p.preventDefault(),m()},children:[t.jsxs(ze,{value:r,onChange:p=>{n(p),s&&i(null)},type:"password",isRequired:!0,className:"flex flex-col gap-1",children:[t.jsx(me,{className:"text-sm font-medium text-[var(--otari-ink)]",children:"Master key"}),t.jsx(fe,{placeholder:"otari-mk-… or your master key",autoFocus:!0,autoComplete:"off"})]}),t.jsxs("details",{className:"text-xs text-[var(--otari-muted)]",children:[t.jsx("summary",{className:"cursor-pointer font-medium text-[var(--otari-brand-dark)]",children:"First run? Where to find your key"}),t.jsxs("p",{className:"mt-2 leading-relaxed",children:["If you did not set ",t.jsx("code",{children:"OTARI_MASTER_KEY"}),", Otari generated one and printed it to the server logs on startup. Look for the line ",t.jsx("code",{children:"Your master key:"})," (for example, run"," ",t.jsx("code",{children:"docker logs "}),") and paste it above."]})]}),t.jsx(bt,{error:s}),t.jsx(C,{type:"submit",variant:"primary",fullWidth:!0,isDisabled:!r.trim()||o,children:o?"Signing in…":"Sign in"})]}),t.jsx("p",{className:"text-center text-xs text-[var(--otari-muted)]",children:"The key is sent once to this gateway and exchanged for a session cookie; it is never stored in the browser."}),t.jsx("div",{className:"border-t border-[var(--otari-line)] pt-4 text-center",children:t.jsx($e,{href:"/welcome",className:"text-sm font-medium text-[var(--otari-brand-dark)]",children:"New to Otari? Open the welcome guide"})})]})})})}const Ot=a.lazy(async()=>({default:(await P(async()=>{const{ActivityPage:e}=await import("./ActivityPage-DIACtZv5.js");return{ActivityPage:e}},__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]))).ActivityPage})),Mt=a.lazy(async()=>({default:(await P(async()=>{const{AliasesPage:e}=await import("./AliasesPage-x0NFKgtH.js");return{AliasesPage:e}},__vite__mapDeps([10,1,2,6,4,7,8,11]))).AliasesPage})),Ft=a.lazy(async()=>({default:(await P(async()=>{const{BudgetsPage:e}=await import("./BudgetsPage-D-S9-U_l.js");return{BudgetsPage:e}},__vite__mapDeps([12,1,2,6,4,7,8,11]))).BudgetsPage})),Ut=a.lazy(async()=>({default:(await P(async()=>{const{DocsPage:e}=await import("./DocsPage-BZcIfS8u.js");return{DocsPage:e}},__vite__mapDeps([13,1,2,4]))).DocsPage})),Kt=a.lazy(async()=>({default:(await P(async()=>{const{KeysPage:e}=await import("./KeysPage-CZjqxzex.js");return{KeysPage:e}},__vite__mapDeps([14,1,2,6,4,7,8,11,15]))).KeysPage})),Bt=a.lazy(async()=>({default:(await P(async()=>{const{ModelsPage:e}=await import("./ModelsPage-BkzSZx5b.js");return{ModelsPage:e}},__vite__mapDeps([16,1,2,6,4,8,9]))).ModelsPage})),zt=a.lazy(async()=>({default:(await P(async()=>{const{OverviewIndex:e}=await import("./OverviewPage-BedTU_aZ.js");return{OverviewIndex:e}},__vite__mapDeps([17,1,2,18,5,4,8]))).OverviewIndex})),$t=a.lazy(async()=>({default:(await P(async()=>{const{ProvidersPage:e}=await import("./ProvidersPage-BP60GuZv.js");return{ProvidersPage:e}},__vite__mapDeps([19,1,2,11,4,8]))).ProvidersPage})),Qt=a.lazy(async()=>({default:(await P(async()=>{const{SettingsPage:e}=await import("./SettingsPage-DR0J2MHH.js");return{SettingsPage:e}},__vite__mapDeps([20,1,2,4]))).SettingsPage})),Vt=a.lazy(async()=>({default:(await P(async()=>{const{ToolsGuardrailsPage:e}=await import("./ToolsGuardrailsPage-DiZ1TbOa.js");return{ToolsGuardrailsPage:e}},__vite__mapDeps([21,1,2,4]))).ToolsGuardrailsPage})),Wt=a.lazy(async()=>({default:(await P(async()=>{const{UsagePage:e}=await import("./UsagePage-Ce_jAzFl.js");return{UsagePage:e}},__vite__mapDeps([22,1,2,3,4,5,6,18,7,8,9]))).UsagePage})),Ht=a.lazy(async()=>({default:(await P(async()=>{const{UsersPage:e}=await import("./UsersPage-5kctrw19.js");return{UsersPage:e}},__vite__mapDeps([23,1,2,6,4,7,8,11,15]))).UsersPage}));function E(e){return t.jsx(a.Suspense,{fallback:t.jsx("div",{role:"status",children:"Loading page…"}),children:e})}function Gt(){const{isAuthenticated:e}=Z();return e?t.jsx(Oe,{children:t.jsx(Me,{children:t.jsxs(w,{element:t.jsx(Rt,{}),children:[t.jsx(w,{index:!0,element:E(t.jsx(zt,{}))}),t.jsx(w,{path:"providers",element:E(t.jsx($t,{}))}),t.jsx(w,{path:"keys",element:E(t.jsx(Kt,{}))}),t.jsx(w,{path:"users",element:E(t.jsx(Ht,{}))}),t.jsx(w,{path:"budgets",element:E(t.jsx(Ft,{}))}),t.jsx(w,{path:"activity",element:E(t.jsx(Ot,{}))}),t.jsx(w,{path:"usage",element:E(t.jsx(Wt,{}))}),t.jsx(w,{path:"models",element:E(t.jsx(Bt,{}))}),t.jsx(w,{path:"aliases",element:E(t.jsx(Mt,{}))}),t.jsx(w,{path:"tools",element:E(t.jsx(Vt,{}))}),t.jsx(w,{path:"settings",element:E(t.jsx(Qt,{}))}),t.jsx(w,{path:"docs",element:E(t.jsx(Ut,{}))}),t.jsx(w,{path:"*",element:t.jsx(Fe,{to:"/",replace:!0})})]})})}):t.jsx(Dt,{})}function Jt({children:e}){const[r]=a.useState(()=>new Ae({defaultOptions:{queries:{refetchOnWindowFocus:!1,retry:(n,s)=>s instanceof T&&(s.status===401||s.status===403)?!1:n<2}}}));return t.jsx(qe,{client:r,children:t.jsx(Ye,{children:e})})}const ke=document.getElementById("root");if(!ke)throw new Error("Root element #root not found");Qe.createRoot(ke).render(t.jsx(a.StrictMode,{children:t.jsx(Jt,{children:t.jsx(Gt,{})})}));export{ur as $,kr as A,Jr as B,ln as C,Er as D,bt as E,cn as F,gt as G,Yr as H,jt as I,at as J,Gr as K,nr as L,on as M,ir as N,en as O,nn as P,Xr as Q,sn as R,tn as S,rn as T,Hr as U,yt as V,pt as W,lr as X,dr as Y,fr as Z,it as _,_r as a,or as a0,cr as a1,ar as a2,sr as a3,hr as a4,Pr as a5,Nr as a6,Cr as a7,gr as a8,mr as a9,br as aa,jr as ab,wr as ac,Zr as ad,Br as ae,Ur as af,zr as b,$r as c,Wr as d,Qr as e,Vr as f,un as g,rr as h,vr as i,pr as j,yr as k,qr as l,Dr as m,Or as n,Mr as o,Kr as p,an as q,Rr as r,Lr as s,Ir as t,Fr as u,Ar as v,Tr as w,tr as x,Sr as y,xr as z}; diff --git a/src/gateway/static/dashboard/index.html b/src/gateway/static/dashboard/index.html index e4ff701b..b9d00afe 100644 --- a/src/gateway/static/dashboard/index.html +++ b/src/gateway/static/dashboard/index.html @@ -6,7 +6,7 @@ Otari Dashboard - + diff --git a/tests/integration/test_require_pricing.py b/tests/integration/test_require_pricing.py index 7db92fc6..316230fa 100644 --- a/tests/integration/test_require_pricing.py +++ b/tests/integration/test_require_pricing.py @@ -85,6 +85,50 @@ def test_unpriced_model_rejected_with_402(strict_pricing_client: TestClient) -> assert _chat(strict_pricing_client, model="openai:gpt-4o", user="priced-user") == 402 +def test_missing_pricing_rejection_is_recorded_in_the_usage_log(strict_pricing_client: TestClient) -> None: + """A 402 rejection is logged as an error row so an operator can see dropped traffic. + + Regression for #317: the gate refunded the reservation and raised without + writing anything, so requests dropped for missing pricing were invisible to + every admin view (the activity log, the error rate, the pricing alarm's + count). Cost stays null: nothing was spent. + """ + c = strict_pricing_client + c.post("/v1/users", json={"user_id": "priced-user"}, headers=_MASTER_HEADER) + assert _chat(c, model="openai:gpt-4o", user="priced-user") == 402 + + rows = c.get("/v1/usage", params={"status": "error"}, headers=_MASTER_HEADER).json() + assert len(rows) == 1 + assert rows[0]["model"] == "openai:gpt-4o" + assert rows[0]["endpoint"] == "/v1/chat/completions" + assert rows[0]["user_id"] == "priced-user" + assert rows[0]["status"] == "error" + assert rows[0]["cost"] is None + assert "pricing" in rows[0]["error_message"].lower() + + # The dashboard's live "N failed in the last hour" signal reads this count. + count = c.get("/v1/usage/count", params={"status": "error"}, headers=_MASTER_HEADER).json() + assert count["total"] == 1 + + +def test_passthrough_missing_pricing_rejection_is_recorded_too(strict_pricing_client: TestClient) -> None: + """The pass-through gate (embeddings, images, rerank) records its 402 as well, + so the failure count covers every rejected request, not only chat.""" + c = strict_pricing_client + c.post("/v1/users", json={"user_id": "priced-user"}, headers=_MASTER_HEADER) + resp = c.post( + "/v1/embeddings", + json={"model": "openai:text-embedding-3-small", "input": "hi", "user": "priced-user"}, + headers=_MASTER_HEADER, + ) + assert resp.status_code == 402 + + rows = c.get("/v1/usage", params={"status": "error"}, headers=_MASTER_HEADER).json() + assert len(rows) == 1 + assert rows[0]["endpoint"] == "/v1/embeddings" + assert rows[0]["cost"] is None + + def test_priced_model_passes_the_gate(strict_pricing_client: TestClient) -> None: """A priced model clears the pricing gate (no 402); any later failure is a provider error.""" strict_pricing_client.post("/v1/users", json={"user_id": "priced-user"}, headers=_MASTER_HEADER) diff --git a/web/src/api/hooks.ts b/web/src/api/hooks.ts index 35b73041..47a794dd 100644 --- a/web/src/api/hooks.ts +++ b/web/src/api/hooks.ts @@ -1,6 +1,7 @@ import { keepPreviousData, useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { apiFetch } from "@/api/client"; +import { isoAgo } from "@/lib/timeRange"; import type { AliasResponse, ApiKey, @@ -683,6 +684,32 @@ export function useUsageCount(filters: UsageFilters, enabled = true) { }); } +// How often the rolling failure count re-reads. A dropped-traffic signal is only +// useful if it moves while the operator watches it. +const FAILURE_COUNT_POLL_MS = 60_000; + +// Requests that failed within the last `windowSeconds`, as a live count. The +// window is resolved inside the query function, not in the key, for two reasons: +// the key stays stable (a "now"-derived key would mint a new cache entry on every +// render), and every refetch re-anchors, so a tab left open keeps reporting the +// last hour rather than quietly widening to the last hour and a half. +export function useFailureCount(windowSeconds: number, enabled = true) { + return useQuery({ + queryKey: [USAGE, "count", "failures", windowSeconds], + queryFn: () => { + const filters: UsageFilters = { status: "error", start_date: isoAgo(windowSeconds) }; + return apiFetch(`/v1/usage/count?${usageParams(filters).toString()}`); + }, + enabled, + refetchInterval: FAILURE_COUNT_POLL_MS, + refetchOnWindowFocus: true, + staleTime: 0, + // A failed count is not worth surfacing: it sits beside its own alarm, and + // the next poll retries anyway. + retry: false, + }); +} + // Delete imported usage rows by selection (ids or by_filter). Only rows the // server treats as imported (counts_toward_budget = false) are removed; every // usage view is invalidated so the list, count, and analytics refresh. diff --git a/web/src/components/PricingWarning.test.tsx b/web/src/components/PricingWarning.test.tsx index deb7e1b4..2e62bfe4 100644 --- a/web/src/components/PricingWarning.test.tsx +++ b/web/src/components/PricingWarning.test.tsx @@ -2,6 +2,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import type { ReactElement } from "react"; +import { MemoryRouter } from "react-router-dom"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { GatewaySettings } from "@/api/types"; @@ -22,7 +23,7 @@ function jsonResponse(body: unknown, status = 200): Response { return new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json" } }); } -function mockSettings(settings: GatewaySettings) { +function mockSettings(settings: GatewaySettings, rejectedInLastHour = 0) { let current = { ...settings }; return vi.spyOn(globalThis, "fetch").mockImplementation(async (input, init) => { const url = String(input); @@ -31,13 +32,30 @@ function mockSettings(settings: GatewaySettings) { if (method === "PATCH") current = { ...current, ...JSON.parse(String(init?.body)) }; return jsonResponse(current); } + if (url.includes("/v1/usage/count")) { + return jsonResponse({ total: rejectedInLastHour }); + } return jsonResponse({}); }); } +type FetchMock = ReturnType; + +// The `start_date` each /v1/usage/count request asked for, in call order. +function countWindows(fetchMock: FetchMock): string[] { + return fetchMock.mock.calls + .map(([u]) => String(u)) + .filter((u) => u.includes("/v1/usage/count")) + .map((u) => String(new URLSearchParams(u.split("?")[1]).get("start_date"))); +} + function renderPage(ui: ReactElement) { const client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); - return render({ui}); + return render( + + {ui} + , + ); } describe("PricingWarning", () => { @@ -69,6 +87,68 @@ describe("PricingWarning", () => { expect(screen.queryByText(/Requests are rejected/)).not.toBeInTheDocument(); }); + it("reports how many requests failed in the last hour and links to them", async () => { + const fetchMock = mockSettings({ ...BASE, require_pricing: true, default_pricing: false }, 12); + renderPage(); + + expect(await screen.findByText("12 requests failed in the last hour.")).toBeInTheDocument(); + expect(screen.getByRole("link", { name: "View failed requests" })).toHaveAttribute( + "href", + "/activity?status=error&range=1h", + ); + + // Counted from the error rows of the last hour, not from all usage. + const countUrl = String(fetchMock.mock.calls.find(([u]) => String(u).includes("/v1/usage/count"))?.[0]); + expect(new URLSearchParams(countUrl.split("?")[1]).get("status")).toBe("error"); + const since = new Date(countWindows(fetchMock)[0]).getTime(); + expect(Date.now() - since).toBeGreaterThan(0); + expect(Date.now() - since).toBeLessThanOrEqual(3_600_000 + 5_000); + }); + + it("re-anchors the window as it polls, so a long-open tab still reports the last hour", async () => { + vi.useFakeTimers({ shouldAdvanceTime: true }); + try { + const fetchMock = mockSettings({ ...BASE, require_pricing: true, default_pricing: false }, 12); + renderPage(); + + await waitFor(() => expect(countWindows(fetchMock)).toHaveLength(1)); + await vi.advanceTimersByTimeAsync(60_000); + await waitFor(() => expect(countWindows(fetchMock).length).toBeGreaterThan(1)); + + // A frozen anchor would ask for the same start_date forever, turning "the + // last hour" into "everything since this tab was opened". + const [first, second] = countWindows(fetchMock); + expect(new Date(second).getTime() - new Date(first).getTime()).toBeGreaterThanOrEqual(59_000); + } finally { + vi.useRealTimers(); + } + }); + + it("stays a config note, with no count, while nothing is failing", async () => { + mockSettings({ ...BASE, require_pricing: true, default_pricing: false }, 0); + renderPage(); + + await screen.findByRole("button", { name: "Enable default pricing" }); + await waitFor(() => expect(globalThis.fetch).toHaveBeenCalledTimes(2)); + expect(screen.queryByText(/failed in the last hour/)).not.toBeInTheDocument(); + expect(screen.queryByRole("link", { name: "View failed requests" })).not.toBeInTheDocument(); + }); + + it("singularizes a lone failure", async () => { + mockSettings({ ...BASE, require_pricing: true, default_pricing: false }, 1); + renderPage(); + + expect(await screen.findByText("1 request failed in the last hour.")).toBeInTheDocument(); + }); + + it("does not count failures while the alarm is quiet", async () => { + const fetchMock = mockSettings({ ...BASE, require_pricing: true, default_pricing: true }, 12); + renderPage(); + + await waitFor(() => expect(globalThis.fetch).toHaveBeenCalled()); + expect(fetchMock.mock.calls.some(([u]) => String(u).includes("/v1/usage/count"))).toBe(false); + }); + it("can be dismissed", async () => { mockSettings({ ...BASE, require_pricing: true, default_pricing: false }); const user = userEvent.setup(); diff --git a/web/src/components/PricingWarning.tsx b/web/src/components/PricingWarning.tsx index fe8555b3..5745464c 100644 --- a/web/src/components/PricingWarning.tsx +++ b/web/src/components/PricingWarning.tsx @@ -1,8 +1,10 @@ import { Button } from "@heroui/react"; import { useState } from "react"; +import { NavLink } from "react-router-dom"; -import { useSettings, useUpdateSettings } from "@/api/hooks"; +import { useFailureCount, useSettings, useUpdateSettings } from "@/api/hooks"; import { InfoBanner } from "@/components/ui"; +import { HOUR_S } from "@/lib/timeRange"; // A gateway-wide alarm, shown on every management page: when `require_pricing` is // on but `default_pricing` is off, every request for an unpriced model is being @@ -10,13 +12,28 @@ import { InfoBanner } from "@/components/ui"; // operator sees it regardless of where they are, or how the state arose (e.g. // flipping require_pricing on via config long after onboarding). Dismissible per // tab; it reappears on reload while the condition holds. +// +// The banner also carries a live count of requests that failed in the last hour, +// so the reject state reads as an active incident (traffic is being dropped right +// now) rather than a static config note, and links into the activity log filtered +// to those failures. export function PricingWarning() { const settings = useSettings(); const updateSettings = useUpdateSettings(); const [dismissed, setDismissed] = useState(false); const needsPricing = settings.data?.require_pricing === true && settings.data.default_pricing === false; - if (!needsPricing || dismissed) { + const showing = needsPricing && !dismissed; + + // Every failure class is counted (402 no pricing, 403 budget or model access, + // 502 provider), not only the pricing rejections: the operator's question in + // this state is "is traffic getting through", and over-reporting a failure is + // safer than a banner reading "0" while requests are being dropped. Only polled + // while the alarm is up. + const failures = useFailureCount(HOUR_S, showing); + const failureCount = failures.data?.total ?? 0; + + if (!showing) { return null; } @@ -27,6 +44,17 @@ export function PricingWarning() { Requests are rejected until pricing is set (require_pricing is on). Enable default pricing to meter new models with public rates right away. + {failureCount > 0 ? ( + <> + {" "} + + {failureCount.toLocaleString()} {failureCount === 1 ? "request" : "requests"} failed in the last hour. + {" "} + + View failed requests + + + ) : null}