tokentracker-cli 0.59.0 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dashboard/dist/assets/{ActivityHeatmap-DrF_K5ys.js → ActivityHeatmap-DxRcGqb0.js} +1 -1
- package/dashboard/dist/assets/{Card-B13p4G_t.js → Card-CSvrNnBU.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-BCWtoeRf.js → DashboardPage-Cj-7LbVz.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-Be88eLBY.js → DevicePage-MIwh75pN.js} +1 -1
- package/dashboard/dist/assets/DialogTitle-5Pz9G5vx.js +1 -0
- package/dashboard/dist/assets/{FadeIn-BEym_KpF.js → FadeIn-BswCXVn8.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-Ckhzj9HH.js → HeaderGithubStar-DcEFKMNV.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-CJb8fFzg.js → IpCheckPage-DNiWD9VI.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-CjTv2ISs.js → LandingPage-mipacgyL.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardAvatar-Cf0GnSTi.js → LeaderboardAvatar-Cha0prSv.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-rIE2Ygw4.js → LeaderboardPage-DB4L0VDC.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfileModal-CT876wK9.js → LeaderboardProfileModal-CXBulhhd.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-EyJsH0Yf.js → LeaderboardProfilePage-BHYi-9Nl.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-BjD-_Cbj.js +2 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-Dz8c-3ZD.js → LocalOnlyNotice-C4z5PX1u.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-iekpEtgT.js → LoginPage-awg65x0b.js} +1 -1
- package/dashboard/dist/assets/PopoverPopup-D-wymWwQ.js +1 -0
- package/dashboard/dist/assets/{ResetPasswordPage-B69CFuJ-.js → ResetPasswordPage-DBCOMvii.js} +1 -1
- package/dashboard/dist/assets/{Select-SwlPWXxZ.js → Select-CkunmlOy.js} +1 -1
- package/dashboard/dist/assets/SelectItemText-D7T3VeBt.js +1 -0
- package/dashboard/dist/assets/{SettingsPage-_spYM3Kx.js → SettingsPage-DIsoKZO8.js} +1 -1
- package/dashboard/dist/assets/SkillsPage-DIGp5OHK.js +1 -0
- package/dashboard/dist/assets/{WidgetsPage-Cs0kJgks.js → WidgetsPage-N_zvVc5J.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-9EChVGEl.js → WrappedPage-DKH7yGKN.js} +1 -1
- package/dashboard/dist/assets/{agent-logos-LiqKeIhs.js → agent-logos-CcHm05Tf.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-B7ItQsbP.js → arrow-up-right-Srji5q6H.js} +1 -1
- package/dashboard/dist/assets/{download-ZBU9wTAZ.js → download-D3OfnpNz.js} +1 -1
- package/dashboard/dist/assets/{info-CExabEBS.js → info-lMniNm1U.js} +1 -1
- package/dashboard/dist/assets/main-BGYbJv-h.js +1034 -0
- package/dashboard/dist/assets/use-limits-display-prefs-D9l2vQL-.js +1 -0
- package/dashboard/dist/assets/{use-native-settings-C3pXNNqf.js → use-native-settings-CtMHe825.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-8f1hUzNE.js → use-usage-limits-CsWdVm7H.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-CrMUx8Pg.js → useCurrency-8LyGElzm.js} +1 -1
- package/dashboard/dist/assets/useScrollLock-CuXeF5Ev.js +1 -0
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/cli.js +2 -2
- package/src/commands/status.js +4 -0
- package/src/commands/sync.js +2 -2
- package/src/lib/diagnostics.js +3 -0
- package/src/lib/openclaw-session-plugin.js +54 -2
- package/src/lib/opencode-go-limits.js +261 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/usage-limits.js +9 -1
- package/dashboard/dist/assets/DialogTitle-BZmopQRt.js +0 -1
- package/dashboard/dist/assets/LimitsPage-DNg_cqiC.js +0 -2
- package/dashboard/dist/assets/PopoverPopup-BhT2-G0O.js +0 -1
- package/dashboard/dist/assets/SelectItemText-DpUKUC8A.js +0 -1
- package/dashboard/dist/assets/SkillsPage-CfTvatpr.js +0 -1
- package/dashboard/dist/assets/main-CgiMNMS_.js +0 -1030
- package/dashboard/dist/assets/use-limits-display-prefs-bSTssszE.js +0 -1
- package/dashboard/dist/assets/useScrollLock-BaJCW9Ak.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{G as u,r as a,a_ as M,a$ as G,b0 as j,b1 as J}from"./main-BGYbJv-h.js";const B=["claude","codex","cursor","gemini","kimi","kiro","grok","copilot","antigravity","zcode","opencodeGo"],q={claude:"CLAUDE",codex:"CODEX",cursor:"CURSOR",gemini:"GEMINI",kimi:"KIMI",kiro:"KIRO",grok:"GROK",copilot:"COPILOT",antigravity:"ANTIGRAVITY",zcode:"ZCODE",opencodeGo:"OPENCODE"};function se(e){return q[e]||null}function ae(e){switch(e){case"claude":return u("limits.provider.claude");case"codex":return u("limits.provider.codex");case"cursor":return u("limits.provider.cursor");case"gemini":return u("limits.provider.gemini");case"kimi":return u("limits.provider.kimi");case"kiro":return u("limits.provider.kiro");case"grok":return u("limits.provider.grok");case"copilot":return u("limits.provider.copilot");case"antigravity":return u("limits.provider.antigravity");case"zcode":return u("limits.provider.zcode");case"opencodeGo":return u("limits.provider.opencode_go");default:return String(e||"")}}const y=B,E="tt.limits.providerOrder",A="tt.limits.providerVisibility",h="tt.limits.displayMode",S="tt.limits.updatedAt",V="limitsPreferences",F="limitsDisplayMode",v=Object.freeze({USED:"used",REMAINING:"remaining"}),g=new Set(Object.values(v)),L=new Set([E,A,h,S]);function I(){return[...y]}function b(){return Object.fromEntries(y.map(e=>[e,!0]))}function _(e){const r=[];if(Array.isArray(e))for(const n of e)y.includes(n)&&!r.includes(n)&&r.push(n);for(const n of y)r.includes(n)||r.push(n);return r}function N(e){const r=b();if(!e||typeof e!="object"||Array.isArray(e))return r;for(const n of y)typeof e[n]=="boolean"&&(r[n]=e[n]);return r}function X(e){return g.has(e)?e:v.USED}function m(e){if(e!=null){if(typeof e=="number")return Number.isSafeInteger(e)?e:void 0;if(typeof e=="string"){const r=e.trim();if(r==="")return;const n=Number(r);return Number.isSafeInteger(n)?n:void 0}}}function O(e={}){const r=e&&typeof e=="object"?e:{};return{displayMode:X(r.displayMode),providerOrder:_(r.providerOrder),providerVisibility:N(r.providerVisibility),updatedAt:m(r.updatedAt)}}function Z(){if(typeof window>"u")return I();try{const e=window.localStorage.getItem(E);return _(e?JSON.parse(e):void 0)}catch{return I()}}function $(){if(typeof window>"u")return b();try{const e=window.localStorage.getItem(A);return N(e?JSON.parse(e):void 0)}catch{return b()}}function H(){if(typeof window>"u")return v.USED;try{const e=window.localStorage.getItem(h);return g.has(e)?e:v.USED}catch{return v.USED}}function Q(){if(!(typeof window>"u"))try{return m(window.localStorage.getItem(S))}catch{return}}function W(){if(typeof window>"u")return!1;try{for(const e of L)if(window.localStorage.getItem(e)!==null)return!0}catch{return!1}return!1}function w(){return O({displayMode:H(),providerOrder:Z(),providerVisibility:$(),updatedAt:Q()})}function ee(e){if(typeof window>"u")return;const r=O(e);try{window.localStorage.setItem(E,JSON.stringify(r.providerOrder)),window.localStorage.setItem(A,JSON.stringify(r.providerVisibility)),window.localStorage.setItem(h,r.displayMode),r.updatedAt===void 0?window.localStorage.removeItem(S):window.localStorage.setItem(S,String(r.updatedAt))}catch(n){console.warn("[tokentracker] limits preferences localStorage write failed:",n)}}function re(e){const r=O(e);return{displayMode:r.displayMode,providerOrder:[...r.providerOrder],providerVisibility:{...r.providerVisibility},updatedAt:r.updatedAt??null}}function te(...e){let r;for(const s of e){const l=m(s);l!==void 0&&(r=r===void 0?l:Math.max(r,l))}const n=Date.now();return r!==void 0&&n<=r?r+1:n}function ie(e,r){return e.length===r.length&&e.every((n,s)=>n===r[s])}function ne(e,r){return y.every(n=>e[n]===r[n])}function C(e,r){return e.displayMode===r.displayMode&&ie(e.providerOrder,r.providerOrder)&&ne(e.providerVisibility,r.providerVisibility)}function oe(e,r){return C(e,r)&&m(e.updatedAt)===m(r.updatedAt)}function k(e,r){const n=m(e.updatedAt),s=m(r.updatedAt);return n!==void 0&&(s===void 0||n>s)}function ce(){const[e,r]=a.useState(w),n=a.useRef(e),s=a.useCallback((o,t={})=>{const i=O(o);return n.current=i,r(i),t.writeLocal&&ee(i),i},[]),l=a.useCallback(o=>{M()&&G(V,re(o))},[]),c=a.useCallback(o=>{const t=w(),i=k(t,n.current)?s(t):n.current,d=O(o(i));if(C(i,d))return;const p=te(t.updatedAt,i.updatedAt),f=s({...d,updatedAt:p},{writeLocal:!0});l(f)},[s,l]),P=a.useCallback(o=>{g.has(o)&&c(t=>({...t,displayMode:o}))},[c]),D=a.useCallback(o=>{if(!g.has(o))return;const t=w();if(t.updatedAt!==void 0){s(t);return}s({...t,displayMode:o,updatedAt:void 0},{writeLocal:!0})},[s]);a.useEffect(()=>{if(!M())return;const o=j(t=>{const i=t?.[V];if(i&&typeof i=="object"){const d=O(i);if(!W()){s(d,{writeLocal:!0});return}const p=w();if(k(d,p))s(d,{writeLocal:!0});else{const f=s(p);oe(d,p)||l(f)}return}D(t?.[F])});return J(),o},[D,s,l]),a.useEffect(()=>{if(typeof window>"u")return;const o=t=>{(t.key===null||L.has(t.key))&&s(w())};return window.addEventListener("storage",o),()=>window.removeEventListener("storage",o)},[s]);const R=a.useCallback(o=>{y.includes(o)&&c(t=>({...t,providerVisibility:{...t.providerVisibility,[o]:!t.providerVisibility[o]}}))},[c]),x=a.useCallback(o=>{c(t=>{const i=t.providerOrder.indexOf(o);if(i<=0)return t;const d=[...t.providerOrder];return[d[i-1],d[i]]=[d[i],d[i-1]],{...t,providerOrder:d}})},[c]),U=a.useCallback(o=>{c(t=>{const i=t.providerOrder.indexOf(o);if(i<0||i>=t.providerOrder.length-1)return t;const d=[...t.providerOrder];return[d[i],d[i+1]]=[d[i+1],d[i]],{...t,providerOrder:d}})},[c]),T=a.useCallback((o,t)=>{o!==t&&c(i=>{const d=i.providerOrder.indexOf(o),p=i.providerOrder.indexOf(t);if(d<0||p<0)return i;const f=[...i.providerOrder],[z]=f.splice(d,1);return f.splice(p,0,z),{...i,providerOrder:f}})},[c]),Y=a.useCallback(()=>{c(()=>({displayMode:v.USED,providerOrder:I(),providerVisibility:b()}))},[c]),K=a.useMemo(()=>e.providerOrder.filter(o=>e.providerVisibility[o]!==!1),[e.providerOrder,e.providerVisibility]);return{order:e.providerOrder,visibility:e.providerVisibility,displayMode:e.displayMode,setDisplayMode:P,visibleOrdered:K,toggle:R,moveUp:x,moveDown:U,moveToward:T,reset:Y}}export{v as L,B as a,se as b,ae as l,ce as u};
|
package/dashboard/dist/assets/{use-native-settings-C3pXNNqf.js → use-native-settings-CtMHe825.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as a,V as x,r as n,aD as g,
|
|
1
|
+
import{j as a,V as x,r as n,aD as g,c3 as m,b0 as b,b1 as u,a$ as f,c2 as h}from"./main-BGYbJv-h.js";import{C as y}from"./Card-CSvrNnBU.js";function k({checked:s,onChange:t,disabled:e,ariaLabel:i}){return a.jsx("button",{type:"button",role:"switch","aria-checked":s,"aria-label":i,onClick:t,disabled:e,className:x("relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-oai-brand-500 disabled:opacity-50 disabled:cursor-not-allowed",s?"bg-oai-brand-500":"bg-oai-gray-300 dark:bg-oai-gray-700"),children:a.jsx("span",{className:x("inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform",s?"translate-x-[18px]":"translate-x-[3px]")})})}function p({label:s,hint:t,control:e}){return a.jsxs("div",{className:"flex items-center justify-between gap-4 py-3",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("div",{className:"text-sm text-oai-gray-900 dark:text-oai-gray-200",children:s}),t?a.jsx("div",{className:"mt-0.5 text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),a.jsx("div",{className:"shrink-0",children:e})]})}function N({title:s,subtitle:t,action:e,children:i}){return a.jsxs(y,{children:[a.jsxs("div",{className:"mb-3 flex items-start justify-between gap-4",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("h2",{className:"text-sm font-medium text-oai-gray-500 dark:text-oai-gray-300 uppercase tracking-wide",children:s}),t?a.jsx("p",{className:"mt-1 truncate text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),e?a.jsx("div",{className:"shrink-0",children:e}):null]}),a.jsx("div",{className:"-mb-3 divide-y divide-oai-gray-200/60 dark:divide-oai-gray-800/60",children:i})]})}function w({options:s,value:t,onChange:e}){return a.jsx("div",{className:"inline-flex items-center rounded-lg border border-oai-gray-200 bg-oai-gray-50 p-0.5 dark:border-oai-gray-800 dark:bg-oai-gray-900",children:s.map(({value:i,label:d,Icon:l})=>{const r=t===i;return a.jsxs("button",{type:"button",onClick:()=>e(i),"aria-pressed":r,className:x("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors",r?"bg-white text-oai-black shadow-sm dark:bg-oai-gray-800 dark:text-white":"text-oai-gray-500 hover:text-oai-black dark:text-oai-gray-400 dark:hover:text-white"),children:[l?a.jsx(l,{className:"h-3.5 w-3.5","aria-hidden":!0}):null,a.jsx("span",{children:d})]},i)})})}function S(){const[s,t]=n.useState(null),e=g()&&m();n.useEffect(()=>{if(!e)return;const r=b(o=>t(o));return u(),r},[e]);const i=n.useCallback((r,o)=>{e&&(t(c=>c&&{...c,[r]:o}),f(r,o))},[e]),d=n.useCallback(r=>{e&&h(r)},[e]),l=n.useCallback(()=>{e&&u()},[e]);return{available:e,settings:s,setSetting:i,runAction:d,refresh:l}}export{N as S,k as T,p as a,w as b,S as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as a,ab as w,aI as c}from"./main-
|
|
1
|
+
import{r as a,ab as w,aI as c}from"./main-BGYbJv-h.js";function v(i){const o=!!i?.initialState,[b,l]=a.useState(()=>o?i?.initialState?.data??null:null),[m,s]=a.useState(()=>o?i?.initialState?.error??null:null),[y,g]=a.useState(!o),u=!!i?.initialRefresh,f=!!i?.publishToPreloadCache,r=a.useCallback((e,t)=>{!f||!e||typeof e!="object"||w(e,{source:t})},[f]),S=a.useCallback(async()=>{try{const e=await c({refresh:!0}),t=e&&typeof e=="object"?e:null;l(t),s(null),r(t,"manual-refresh")}catch(e){s(e?.message||String(e))}},[r]),d=a.useCallback(async()=>{try{const e=await c(),t=e&&typeof e=="object"?e:null;l(t),s(null),r(t,"page-load")}catch(e){s(e?.message||String(e))}},[r]);return a.useEffect(()=>{if(typeof window>"u"||typeof document>"u")return;const e=15e3;let t=Date.now();const n=()=>{if(document.visibilityState!=="visible")return;const h=Date.now();h-t<e||(t=h,d())};return window.addEventListener("focus",n),document.addEventListener("visibilitychange",n),()=>{window.removeEventListener("focus",n),document.removeEventListener("visibilitychange",n)}},[d]),a.useEffect(()=>{if(o&&!u)return;let e=!1;return(async()=>{try{const t=await c();if(e)return;const n=t&&typeof t=="object"?t:null;l(n),s(null),r(n,"page-load")}catch(t){if(e)return;s(t?.message||String(t))}finally{e||g(!1)}})(),()=>{e=!0}},[o,u,r]),{data:b,error:m,isLoading:y,refresh:S}}export{v as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e,
|
|
1
|
+
import{r as e,c0 as t,c1 as r,K as c}from"./main-BGYbJv-h.js";const s=Object.freeze({currency:c,rate:1,symbol:"$",rates:{...r},rateSource:"default",rateFetchedAt:null,setCurrency:()=>{}});function a(){return e.useContext(t)??s}export{a as u};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as _t,cK as Ve,cE as Dt,b5 as Ot,b3 as Y,b6 as wt,bC as U,cL as Ct,cf as je,j as Se,ck as ze,r as g,bA as X,cm as ot,cM as Gt,b7 as it,bz as Le,c4 as Kt,cl as ct,cN as xt,bE as x,bD as ke,b$ as he,cO as Xe,cP as le,cq as ae,bI as C,bJ as Ut,cD as It,cH as Je,c5 as Vt,bW as ut,bR as lt,bT as Xt,cQ as qt,bF as be,bV as $t,cy as jt,b as Pe,bO as zt,cR as Qe,ba as Be,cS as Jt,cT as Ye,bL as Qt,by as We,bM as Zt,bB as Ie,cb as _e,ca as en,cU as tn,c8 as nn,bK as rn,bP as Te,cV as me,bd as sn,be as on,b9 as De,cW as cn,cX as at,cB as un,cg as Ze}from"./main-BGYbJv-h.js";const ln=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],zn=_t("check",ln);function Fe(e,t,n=!0){return e.filter(s=>s.parentId===t).flatMap(s=>[...!n||s.context?.open?[s]:[],...Fe(e,s.id,n)])}function ft(e,t){let n=[],r=e.find(s=>s.id===t)?.parentId;for(;r;){const s=e.find(o=>o.id===r);r=s?.parentId,s&&(n=n.concat(s))}return n}function an(e){e.preventDefault(),e.stopPropagation()}function fn(e){return"nativeEvent"in e}function dn(e){return e.pointerType===""&&e.isTrusted?!0:Ve&&e.pointerType?e.type==="click"&&e.buttons===1:e.detail===0&&!e.pointerType}function pn(e){return Dt?!1:!Ve&&e.width===0&&e.height===0||Ve&&e.width===1&&e.height===1&&e.pressure===0&&e.detail===0&&e.pointerType==="mouse"||e.width<1&&e.height<1&&e.pressure===0&&e.detail===0&&e.pointerType==="touch"}function Jn(e,t){const n=["mouse","pen"];return t||n.push("",void 0),n.includes(e)}function gn(e){const t=e.type;return t==="click"||t==="mousedown"||t==="keydown"||t==="keyup"}function Ae(e){const t=Ot(hn,e).current;return t.next=e,Y(t.effect),t}function hn(e){const t={current:e,next:e,effect:()=>{t.current=t.next}};return t}let Ne=0;function Ge(e,t={}){const{preventScroll:n=!1,sync:r=!1,shouldFocus:s}=t;cancelAnimationFrame(Ne);function o(){s&&!s()||e?.focus({preventScroll:n})}if(r)return o(),wt;const i=requestAnimationFrame(o);return Ne=i,()=>{Ne===i&&(cancelAnimationFrame(i),Ne=0)}}const Ke={inert:new WeakMap,"aria-hidden":new WeakMap},dt="data-base-ui-inert",qe={inert:new WeakSet,"aria-hidden":new WeakSet};let xe=new WeakMap,Ue=0;function mn(e){return qe[e]}function kt(e){return e?Ct(e)?e.host:kt(e.parentNode):null}const pt=(e,t)=>t.map(n=>{if(e.contains(n))return n;const r=kt(n);return e.contains(r)?r:null}).filter(n=>n!=null),gt=e=>{const t=new Set;return e.forEach(n=>{let r=n;for(;r&&!t.has(r);)t.add(r),r=r.parentNode}),t},ht=(e,t,n)=>{const r=[],s=o=>{!o||n.has(o)||Array.from(o.children).forEach(i=>{je(i)!=="script"&&(t.has(i)?s(i):r.push(i))})};return s(e),r};function bn(e,t,n,r,{mark:s=!0}){let o=null;r?o="inert":n&&(o="aria-hidden");let i=null,u=null;const h=pt(t,e),f=s?ht(t,gt(h),new Set(h)):[],k=[],W=[];if(o){const b=Ke[o],T=mn(o);u=T,i=b;const A=pt(t,Array.from(t.querySelectorAll("[aria-live]"))),P=h.concat(A);ht(t,gt(P),new Set(P)).forEach(y=>{const E=y.getAttribute(o),Q=E!==null&&E!=="false",H=(b.get(y)||0)+1;b.set(y,H),k.push(y),H===1&&Q&&T.add(y),Q||y.setAttribute(o,o==="inert"?"":"true")})}return s&&f.forEach(b=>{const T=(xe.get(b)||0)+1;xe.set(b,T),W.push(b),T===1&&b.setAttribute(dt,"")}),Ue+=1,()=>{i&&k.forEach(b=>{const A=(i.get(b)||0)-1;i.set(b,A),A||(!u?.has(b)&&o&&b.removeAttribute(o),u?.delete(b))}),s&&W.forEach(b=>{const T=(xe.get(b)||0)-1;xe.set(b,T),T||b.removeAttribute(dt)}),Ue-=1,Ue||(Ke.inert=new WeakMap,Ke["aria-hidden"]=new WeakMap,qe.inert=new WeakSet,qe["aria-hidden"]=new WeakSet,xe=new WeakMap)}}function mt(e,t={}){const{ariaHidden:n=!1,inert:r=!1,mark:s=!0}=t,o=U(e[0]).body;return bn(e,o,n,r,{mark:s})}function Pt(){const e=new Map;return{emit(t,n){e.get(t)?.forEach(r=>r(n))},on(t,n){e.has(t)||e.set(t,new Set),e.get(t).add(n)},off(t,n){e.get(t)?.delete(n)}}}class En{nodesRef={current:[]};events=Pt();addNode(t){this.nodesRef.current.push(t)}removeNode(t){const n=this.nodesRef.current.findIndex(r=>r===t);n!==-1&&this.nodesRef.current.splice(n,1)}}const Ft=g.createContext(null),Mt=g.createContext(null),et=()=>g.useContext(Ft)?.id||null,tt=e=>{const t=g.useContext(Mt);return e??t};function Qn(e){const t=ze(),n=tt(e),r=et();return Y(()=>{if(!t)return;const s={id:t,parentId:r};return n?.addNode(s),()=>{n?.removeNode(s)}},[n,t,r]),t}function Zn(e){const{children:t,id:n}=e,r=et();return Se.jsx(Ft.Provider,{value:g.useMemo(()=>({id:n,parentId:r}),[n,r]),children:t})}function er(e){const{children:t,externalTree:n}=e,r=Ot(()=>n??new En).current;return Se.jsx(Mt.Provider,{value:r,children:t})}function yn(e,t){const n=Be(be(e));return e instanceof n.KeyboardEvent?"keyboard":e instanceof n.FocusEvent?t||"keyboard":"pointerType"in e?e.pointerType||"keyboard":"touches"in e?"touch":e instanceof n.MouseEvent?t||(e.detail===0?"keyboard":"mouse"):""}const bt=20;let fe=[];function nt(){fe=fe.filter(e=>e.deref()?.isConnected)}function Et(e){nt(),e&&je(e)!=="body"&&(fe.push(new WeakRef(e)),fe.length>bt&&(fe=fe.slice(-bt)))}function yt(){return nt(),fe[fe.length-1]?.deref()}function Rn(e){return e?Qe(e)?e:xt(e)[0]||e:null}function Rt(e){if(e.hasAttribute("tabindex")&&!e.hasAttribute("data-tabindex")||!e.getAttribute("role")?.includes("dialog"))return;const n=Jt(e).filter(s=>{const o=s.getAttribute("data-tabindex")||"";return Qe(s)||s.hasAttribute("data-tabindex")&&!o.startsWith("-")}),r=e.getAttribute("tabindex");n.length===0?r!=="0"&&(e.setAttribute("tabindex","0"),e.setAttribute("data-tabindex","0")):(r!=="-1"||e.hasAttribute("data-tabindex")&&e.getAttribute("data-tabindex")!=="-1")&&(e.setAttribute("tabindex","-1"),e.setAttribute("data-tabindex","-1"))}function tr(e){const{context:t,children:n,disabled:r=!1,initialFocus:s=!0,returnFocus:o=!0,restoreFocus:i=!1,modal:u=!0,closeOnFocusOut:h=!0,openInteractionType:f="",nextFocusableElement:k,previousFocusableElement:W,beforeContentFocusGuardRef:b,externalTree:T,getInsideElements:A}=e,P="rootStore"in t?t.rootStore:t,_=P.useState("open"),y=P.useState("domReferenceElement"),E=P.useState("floatingElement"),{events:Q,dataRef:H}=P.context,q=X(()=>H.current.floatingContext?.nodeId),J=s===!1,D=ot(y)&&J,se=Ae(s),F=Ae(o),$=Ae(f),ve=Ae(_),N=tt(T),R=Gt(),B=g.useRef(!1),oe=g.useRef(!1),ee=g.useRef(!1),te=g.useRef(null),Ee=g.useRef(""),de=g.useRef(""),pe=g.useRef(null),ye=g.useRef(null),S=it(pe,b,R?.beforeInsideRef),j=it(ye,R?.afterInsideRef),ie=Le(),ce=Le(),Oe=Kt(),Me=R!=null,p=ct(E),ne=X((m=p)=>m?xt(m):[]),Z=X(()=>A?.().filter(m=>m!=null)??[]);g.useEffect(()=>{if(r||!u)return;function m(L){L.key==="Tab"&&C(p,ae(U(p)))&&ne().length===0&&!D&&an(L)}const I=U(p);return x(I,"keydown",m)},[r,p,u,D,ne]),g.useEffect(()=>{if(r||!_)return;const m=U(p);function I(){ee.current=!1}function L(z){const a=be(z),O=Z(),d=C(E,a)||C(y,a)||C(R?.portalNode,a)||O.some(M=>M===a||C(M,a));ee.current=!d,de.current=z.pointerType||"keyboard",a?.closest(`[${$t}]`)&&(oe.current=!0,ce.start(0,()=>{oe.current=!1}))}function G(){de.current="keyboard"}return ke(x(m,"pointerdown",L,!0),x(m,"pointerup",I,!0),x(m,"pointercancel",I,!0),x(m,"keydown",G,!0),I)},[r,E,y,p,_,R,ce,Z]),g.useEffect(()=>{if(r||!h)return;const m=U(p);function I(){oe.current=!0,ce.start(0,()=>{oe.current=!1})}function L(O){const d=be(O);Qe(d)&&(te.current=d)}function G(O){const d=O.relatedTarget,M=O.currentTarget,K=be(O);u&&d==null&&K!=null&&C(E,K)&&Et(K),queueMicrotask(()=>{const w=q(),Re=P.context.triggerElements,c=Z(),l=d?.hasAttribute(Xe("focus-guard"))&&[pe.current,ye.current,R?.beforeInsideRef.current,R?.afterInsideRef.current,R?.beforeOutsideRef.current,R?.afterOutsideRef.current,le(W),le(k)].includes(d),V=!(C(y,d)||C(E,d)||C(d,E)||C(R?.portalNode,d)||c.some(v=>v===d||C(v,d))||d!=null&&Re.hasElement(d)||Re.hasMatchingElement(v=>C(v,d))||l||N&&(Fe(N.nodesRef.current,w).find(v=>C(v.context?.elements.floating,d)||C(v.context?.elements.domReference,d))||ft(N.nodesRef.current,w).find(v=>[v.context?.elements.floating,ct(v.context?.elements.floating)].includes(d)||v.context?.elements.domReference===d)));if(M===y&&p&&Rt(p),i&&M!==y&&!jt(K)&&ae(m)===m.body){if(he(p)&&(p.focus(),i==="popup")){Oe.request(()=>{p.focus()});return}const v=ne(),ue=te.current,we=(ue&&v.includes(ue)?ue:null)||v[v.length-1]||p;he(we)&&we.focus()}if(H.current.insideReactTree){H.current.insideReactTree=!1;return}(D||!u)&&d&&V&&!oe.current&&(D||d!==yt())&&(B.current=!0,P.setOpen(!1,Pe(zt,O)))})}function z(){ee.current||(H.current.insideReactTree=!0,ie.start(0,()=>{H.current.insideReactTree=!1}))}const a=he(y)?y:null;if(!(!E&&!a))return ke(a&&x(a,"focusout",G),a&&x(a,"pointerdown",I),E&&x(E,"focusin",L),E&&x(E,"focusout",G),E&&R&&x(E,"focusout",z,!0))},[r,y,E,p,u,N,R,P,h,i,ne,D,q,H,ie,ce,Oe,k,W,Z]),g.useEffect(()=>{if(r||!E||!_)return;const m=Array.from(R?.portalNode?.querySelectorAll(`[${Xe("portal")}]`)||[]),L=(N?ft(N.nodesRef.current,q()):[]).find(M=>ot(M.context?.elements.domReference||null))?.context?.elements.domReference,z=[...[E,...m,pe.current,ye.current,R?.beforeOutsideRef.current,R?.afterOutsideRef.current,...Z()],L,le(W),le(k),D?y:null].filter(M=>M!=null),a=mt(z,{ariaHidden:u||D,mark:!1}),O=[E,...m].filter(M=>M!=null),d=mt(O);return()=>{d(),a()}},[_,r,y,E,u,R,D,N,q,k,W,Z]),Y(()=>{if(!_||r||!he(p))return;const m=U(p),I=ae(m);queueMicrotask(()=>{const L=se.current,G=typeof L=="function"?L($.current||""):L;if(G===void 0||G===!1||C(p,I))return;let a=null;const O=()=>(a==null&&(a=ne(p)),a[0]||p);let d;G===!0||G===null?d=O():d=le(G),d=d||O();const M=C(p,ae(m));Ge(d,{preventScroll:d===p,shouldFocus(){if(!ve.current)return!1;if(M)return!0;const K=ae(m);return!(K!==d&&C(p,K))}})})},[r,_,p,ne,se,$,ve]),Y(()=>{if(r||!p)return;const m=U(p),I=ae(m),L=$.current==null;Et(I);function G(a){if(a.open||(Ee.current=yn(a.nativeEvent,de.current)),a.reason===Ut&&a.nativeEvent.type==="mouseleave"&&(B.current=!0),a.reason===It)if(a.nested)B.current=!1;else if(dn(a.nativeEvent)||pn(a.nativeEvent))B.current=!1;else{let O=!1;U(p).createElement("div").focus({get preventScroll(){return O=!0,!1}}),O?B.current=!1:B.current=!0}}Q.on("openchange",G);function z(){const a=F.current;let O=typeof a=="function"?a(Ee.current):a;if(O===void 0||O===!1)return null;O===null&&(O=!0);const d=y?.isConnected?y:null,M=I?.isConnected&&je(I)!=="body"?I:null;let K=L?M||d:d||M;return K||(K=yt()||null),typeof O=="boolean"?K:le(O)||K||null}return()=>{Q.off("openchange",G);const a=ae(m),O=Z(),d=C(E,a)||O.some(w=>w===a||C(w,a))||N&&Fe(N.nodesRef.current,q(),!1).some(w=>C(w.context?.elements.floating,a)),M=F.current,K=z();queueMicrotask(()=>{const w=Rn(K),Re=typeof M!="boolean";M&&!B.current&&he(w)&&(!(!Re&&w!==a&&a!==m.body)||d)&&w.focus({preventScroll:!0}),B.current=!1})}},[r,E,p,F,$,Q,N,y,q,Z]),Y(()=>{if(!Je||_||!E)return;const m=ae(U(E));!he(m)||!Vt(m)||C(E,m)&&m.blur()},[_,E]),Y(()=>{if(!(r||!R))return R.setFocusManagerState({modal:u,closeOnFocusOut:h,open:_,onOpenChange:P.setOpen,domReference:y}),()=>{R.setFocusManagerState(null)}},[r,R,u,_,P,h,y]),Y(()=>{if(!(r||!p))return Rt(p),()=>{queueMicrotask(nt)}},[r,p]);const ge=!r&&(u?!D:!0)&&(Me||u);return Se.jsxs(g.Fragment,{children:[ge&&Se.jsx(ut,{"data-type":"inside",ref:S,onFocus:m=>{if(u){const I=ne();Ge(I[I.length-1])}else R?.portalNode&&(B.current=!1,lt(m,R.portalNode)?Xt(y)?.focus():le(W??R.beforeOutsideRef)?.focus())}}),n,ge&&Se.jsx(ut,{"data-type":"inside",ref:j,onFocus:m=>{u?Ge(ne()[0]):R?.portalNode&&(h&&(B.current=!0),lt(m,R.portalNode)?qt(y)?.focus():le(k??R.afterOutsideRef)?.focus())}})]})}function Tn(){return!1}function Sn(e){return{escapeKey:typeof e=="boolean"?e:e?.escapeKey??!1,outsidePress:typeof e=="boolean"?e:e?.outsidePress??!0}}function nr(e,t={}){const{enabled:n=!0,escapeKey:r=!0,outsidePress:s=!0,outsidePressEvent:o="sloppy",referencePress:i=Tn,bubbles:u,externalTree:h}=t,f="rootStore"in e?e.rootStore:e,k=f.useState("open"),W=f.useState("floatingElement"),{dataRef:b}=f.context,T=tt(h),A=X(typeof s=="function"?s:()=>!1),P=typeof s=="function"?A:s,_=P!==!1,y=X(()=>o),{escapeKey:E,outsidePress:Q}=Sn(u),H=g.useRef(!1),q=g.useRef(!1),J=g.useRef(!1),D=g.useRef(!1),se=g.useRef(""),F=g.useRef(null),$=Le(),ve=Le(),N=X(()=>{ve.clear(),b.current.insideReactTree=!1}),R=X(S=>{const j=b.current.floatingContext?.nodeId;return(T?Fe(T.nodesRef.current,j):[]).some(ce=>ce.context?.open&&!ce.context.dataRef.current[S])}),B=X(S=>Ye(S,f.select("floatingElement"))||Ye(S,f.select("domReferenceElement"))),oe=X(S=>{i()&&f.setOpen(!1,Pe(Qt,S.nativeEvent))}),ee=X(S=>{if(!k||!n||!r||S.key!=="Escape"||D.current||!E&&R("__escapeKeyBubbles"))return;const j=fn(S)?S.nativeEvent:S,ie=Pe(Zt,j);f.setOpen(!1,ie),ie.isCanceled||S.preventDefault(),!E&&!ie.isPropagationAllowed&&S.stopPropagation()}),te=X(()=>{b.current.insideReactTree=!0,ve.start(0,N)}),Ee=X(S=>{if(!k||!n||S.button!==0)return;const j=be(S.nativeEvent);C(f.select("floatingElement"),j)&&(H.current||(H.current=!0,q.current=!1))}),de=X(S=>{!k||!n||(S.defaultPrevented||S.nativeEvent.defaultPrevented)&&H.current&&(q.current=!0)});g.useEffect(()=>{if(!k||!n)return;b.current.__escapeKeyBubbles=E,b.current.__outsidePressBubbles=Q;const S=new We,j=new We;function ie(){S.clear(),D.current=!0}function ce(){S.start(Je?5:0,()=>{D.current=!1})}function Oe(){J.current=!0,j.start(0,()=>{J.current=!1})}function Me(){H.current=!1,q.current=!1}function p(){const c=se.current,l=c==="pen"||!c?"mouse":c,V=y(),v=typeof V=="function"?V():V;return typeof v=="string"?v:v[l]}function ne(c){const l=p();return l==="intentional"&&c.type!=="click"||l==="sloppy"&&c.type==="click"}function Z(c){const l=b.current.floatingContext?.nodeId,V=T&&Fe(T.nodesRef.current,l).some(v=>Ye(c,v.context?.elements.floating));return B(c)||V}function ge(c){if(ne(c)){c.type!=="click"&&!B(c)&&(j.clear(),J.current=!1),N();return}if(b.current.insideReactTree){N();return}const l=be(c),V=`[${Xe("inert")}]`,v=Ie(l)?l.getRootNode():null,ue=Array.from((Ct(v)?v:U(f.select("floatingElement"))).querySelectorAll(V)),we=f.context.triggerElements;if(l&&(we.hasElement(l)||we.hasMatchingElement(re=>C(re,l))))return;let Ce=Ie(l)?l:null;for(;Ce&&!_e(Ce);){const re=en(Ce);if(_e(re)||!Ie(re))break;Ce=re}if(!(ue.length&&Ie(l)&&!tn(l)&&!C(l,f.select("floatingElement"))&&ue.every(re=>!C(Ce,re)))){if(he(l)&&!("touches"in c)){const re=_e(l),He=nn(l),st=/auto|scroll/,At=re||st.test(He.overflowX),Nt=re||st.test(He.overflowY),Lt=At&&l.clientWidth>0&&l.scrollWidth>l.clientWidth,Wt=Nt&&l.clientHeight>0&&l.scrollHeight>l.clientHeight,Bt=He.direction==="rtl",Ht=Wt&&(Bt?c.offsetX<=l.offsetWidth-l.clientWidth:c.offsetX>l.clientWidth),Yt=Lt&&c.offsetY>l.clientHeight;if(Ht||Yt)return}if(!Z(c)){if(p()==="intentional"&&J.current){j.clear(),J.current=!1;return}typeof P=="function"&&!P(c)||R("__outsidePressBubbles")||(f.setOpen(!1,Pe(It,c)),N())}}}function m(c){p()!=="sloppy"||c.pointerType==="touch"||!f.select("open")||!n||B(c)||ge(c)}function I(c){if(p()!=="sloppy"||!f.select("open")||!n||B(c))return;const l=c.touches[0];l&&(F.current={startTime:Date.now(),startX:l.clientX,startY:l.clientY,dismissOnTouchEnd:!1,dismissOnMouseDown:!0},$.start(1e3,()=>{F.current&&(F.current.dismissOnTouchEnd=!1,F.current.dismissOnMouseDown=!1)}))}function L(c,l){const V=be(c);if(!V)return;const v=x(V,c.type,()=>{l(c),v()})}function G(c){se.current="touch",L(c,I)}function z(c){$.clear(),c.type==="pointerdown"&&(se.current=c.pointerType),!(c.type==="mousedown"&&F.current&&!F.current.dismissOnMouseDown)&&L(c,l=>{l.type==="pointerdown"?m(l):ge(l)})}function a(c){if(!H.current)return;const l=q.current;if(Me(),p()==="intentional"){if(c.type==="pointercancel"){l&&Oe();return}if(!Z(c)){if(l){Oe();return}typeof P=="function"&&!P(c)||(j.clear(),J.current=!0,N())}}}function O(c){if(p()!=="sloppy"||!F.current||B(c))return;const l=c.touches[0];if(!l)return;const V=Math.abs(l.clientX-F.current.startX),v=Math.abs(l.clientY-F.current.startY),ue=Math.sqrt(V*V+v*v);ue>5&&(F.current.dismissOnTouchEnd=!0),ue>10&&(ge(c),$.clear(),F.current=null)}function d(c){L(c,O)}function M(c){p()!=="sloppy"||!F.current||B(c)||(F.current.dismissOnTouchEnd&&ge(c),$.clear(),F.current=null)}function K(c){L(c,M)}const w=U(W),Re=ke(r&&ke(x(w,"keydown",ee),x(w,"compositionstart",ie),x(w,"compositionend",ce)),_&&ke(x(w,"click",z,!0),x(w,"pointerdown",z,!0),x(w,"pointerup",a,!0),x(w,"pointercancel",a,!0),x(w,"mousedown",z,!0),x(w,"mouseup",a,!0),x(w,"touchstart",G,!0),x(w,"touchmove",d,!0),x(w,"touchend",K,!0)));return()=>{Re(),S.clear(),j.clear(),Me(),J.current=!1}},[b,W,r,_,P,k,n,E,Q,ee,N,y,R,B,T,f,$]),g.useEffect(N,[P,N]);const pe=g.useMemo(()=>({onKeyDown:ee,onPointerDown:oe,onClick:oe}),[ee,oe]),ye=g.useMemo(()=>({onKeyDown:ee,onPointerDown:de,onMouseDown:de,onClickCapture:te,onMouseDownCapture(S){te(),Ee(S)},onPointerDownCapture(S){te(),Ee(S)},onMouseUpCapture:te,onTouchEndCapture:te,onTouchMoveCapture:te}),[ee,te,Ee,de]);return g.useMemo(()=>n?{reference:pe,floating:ye,trigger:pe}:{},[n,pe,ye])}function vn(e){const t=g.useRef(!0);t.current&&(t.current=!1,e())}const On={open:Te(e=>e.open),transitionStatus:Te(e=>e.transitionStatus),domReferenceElement:Te(e=>e.domReferenceElement),referenceElement:Te(e=>e.positionReference??e.referenceElement),floatingElement:Te(e=>e.floatingElement),floatingId:Te(e=>e.floatingId)};class wn extends rn{constructor(t){const{syncOnly:n,nested:r,onOpenChange:s,triggerElements:o,...i}=t;super({...i,positionReference:i.referenceElement,domReferenceElement:i.referenceElement},{onOpenChange:s,dataRef:{current:{}},events:Pt(),nested:r,triggerElements:o},On),this.syncOnly=n}syncOpenEvent=(t,n)=>{(!t||!this.state.open||n!=null&&gn(n))&&(this.context.dataRef.current.openEvent=t?n:void 0)};dispatchOpenChange=(t,n)=>{this.syncOpenEvent(t,n.event);const r={open:t,reason:n.reason,nativeEvent:n.event,nested:this.context.nested,triggerElement:n.trigger};this.context.events.emit("openchange",r)};setOpen=(t,n)=>{if(this.syncOnly){this.context.onOpenChange?.(t,n);return}this.dispatchOpenChange(t,n),this.context.onOpenChange?.(t,n)}}function Cn(e){const{popupStore:t,treatPopupAsFloatingElement:n=!1,floatingRootContext:r,floatingId:s,nested:o,onOpenChange:i}=e,u=t.useState("open"),h=t.useState("activeTriggerElement"),f=t.useState(n?"popupElement":"positionerElement"),k=t.context.triggerElements,W=i,b=g.useRef(null);r===void 0&&b.current===null&&(b.current=new wn({open:u,transitionStatus:void 0,referenceElement:h,floatingElement:f,triggerElements:k,onOpenChange:W,floatingId:s,syncOnly:!0,nested:o}));const T=r??b.current;return t.useSyncedValue("floatingId",s),Y(()=>{const A={open:u,floatingId:s,referenceElement:h,floatingElement:f};Ie(h)&&(A.domReferenceElement=h),T.state.positionReference===T.state.referenceElement&&(A.positionReference=h),T.update(A)},[u,s,h,f,T]),T.context.onOpenChange=W,T.context.nested=o,T}function xn(e,t=!1,n=!1){const[r,s]=g.useState(e&&t?"idle":void 0),[o,i]=g.useState(e);return e&&!o&&(i(!0),s("starting")),!e&&o&&r!=="ending"&&!n&&s("ending"),!e&&!o&&r==="ending"&&s(void 0),Y(()=>{if(!e&&o&&r!=="ending"&&n){const u=me.request(()=>{s("ending")});return()=>{me.cancel(u)}}},[e,o,r,n]),Y(()=>{if(!e||t)return;const u=me.request(()=>{s(void 0)});return()=>{me.cancel(u)}},[t,e]),Y(()=>{if(!e||!t)return;e&&o&&r!=="idle"&&s("starting");const u=me.request(()=>{s("idle")});return()=>{me.cancel(u)}},[t,e,o,r]),{mounted:o,setMounted:i,transitionStatus:r}}const rr={tabIndex:-1,[cn]:""};function sr(e){return t=>t==="touch"?e.current:!0}function or(e,t,n=!1){const r=ze(),s=et()!=null,o=g.useRef(null);e===void 0&&o.current===null&&(o.current=t(r,s));const i=e??o.current;return Cn({popupStore:i,treatPopupAsFloatingElement:n,floatingRootContext:i.state.floatingRootContext,floatingId:r,nested:s,onOpenChange:i.setOpen}),{store:i,internalStore:o.current}}function In(e,t){const n=g.useRef(null),r=g.useRef(null);return g.useCallback(s=>{if(e===void 0)return;let o=!1;if(n.current!==null){const i=n.current,u=r.current,h=t.context.triggerElements.getById(i);u&&h===u&&(t.context.triggerElements.delete(i),o=!0),n.current=null,r.current=null}if(s!==null&&(n.current=e,r.current=s,t.context.triggerElements.add(e,s),o=!0),o){const i=t.context.triggerElements.size;t.select("open")&&t.state.triggerCount!==i&&t.set("triggerCount",i)}},[t,e])}function ir(e,t,n,r=!1){t?e.preventUnmountingOnClose=!1:r&&(e.preventUnmountingOnClose=!0);const s=n?.id??null;(s||t)&&(e.activeTriggerId=s,e.activeTriggerElement=n??null)}function cr(e){let t=!1;return e.preventUnmountOnClose=()=>{t=!0},()=>t}function ur(e,t,n,r){vn(()=>{t===void 0&&e.state.open===!1&&n&&(e.state={...e.state,open:!0,activeTriggerId:r,preventUnmountingOnClose:!1})})}function lr(e,t,n,r){const s=n.useState("isMountedByTrigger",e),o=In(e,n),i=X(u=>{if(o(u),!u)return;const h=n.select("open"),f=n.select("activeTriggerId");if(f===e){n.update({activeTriggerElement:u,...h?r:null});return}f==null&&h&&n.update({activeTriggerId:e,activeTriggerElement:u,...r})});return Y(()=>{s&&n.update({activeTriggerElement:t.current,...r})},[s,n,t,...Object.values(r)]),{registerTrigger:i,isMountedByThisTrigger:s}}function ar(e,t={}){const{closeOnActiveTriggerUnmount:n=!1}=t,r=e.useState("open"),s=e.useState("triggerCount");Y(()=>{if(!r){e.state.triggerCount!==0&&e.set("triggerCount",0);return}const o=e.context.triggerElements.size,i={};e.state.triggerCount!==o&&(i.triggerCount=o);const u=e.select("activeTriggerId");let h=null;if(u){const f=e.context.triggerElements.getById(u);f?f!==e.state.activeTriggerElement&&(i.activeTriggerElement=f):h=u}if(!h&&!u&&o===1){const f=e.context.triggerElements.entries().next();if(!f.done){const[k,W]=f.value;i.activeTriggerId=k,i.activeTriggerElement=W}}(i.triggerCount!==void 0||i.activeTriggerId!==void 0||i.activeTriggerElement!==void 0)&&e.update(i),h&&n&&queueMicrotask(()=>{if(e.select("open")&&e.select("activeTriggerId")===h&&!e.context.triggerElements.getById(h)){const f=Pe(sn);e.setOpen(!1,f),f.isCanceled||e.update({activeTriggerId:null,activeTriggerElement:null})}})},[r,e,s,n])}function fr(e,t,n){const{mounted:r,setMounted:s,transitionStatus:o}=xn(e),i=t.useState("preventUnmountingOnClose"),u=e?!1:i;t.useSyncedValues({mounted:r,transitionStatus:o,preventUnmountingOnClose:u});const h=X(()=>{s(!1),t.update({activeTriggerId:null,activeTriggerElement:null,mounted:!1,preventUnmountingOnClose:!1}),n?.(),t.context.onOpenChangeComplete?.(!1)});return on({enabled:r&&!e&&!u,open:e,ref:t.context.popupRef,onComplete(){e||h()}}),{forceUnmount:h,transitionStatus:o}}function dr(e,t){e.useSyncedValues(t),Y(()=>()=>{e.update({activeTriggerProps:De,inactiveTriggerProps:De,popupProps:De})},[e])}function pr(e,t){Y(()=>{!t&&e.state.openMethod!==null&&e.set("openMethod",null)},[t,e]),Y(()=>()=>{e.state.openMethod!==null&&e.set("openMethod",null)},[e])}class gr{constructor(){this.elementsSet=new Set,this.idMap=new Map}add(t,n){const r=this.idMap.get(t);r!==n&&(r!==void 0&&this.elementsSet.delete(r),this.elementsSet.add(n),this.idMap.set(t,n))}delete(t){const n=this.idMap.get(t);n&&(this.elementsSet.delete(n),this.idMap.delete(t))}hasElement(t){return this.elementsSet.has(t)}hasMatchingElement(t){for(const n of this.elementsSet)if(t(n))return!0;return!1}getById(t){return this.idMap.get(t)}entries(){return this.idMap.entries()}elements(){return this.elementsSet.values()}get size(){return this.idMap.size}}let rt=(function(e){return e.open="data-open",e.closed="data-closed",e[e.startingStyle=at.startingStyle]="startingStyle",e[e.endingStyle=at.endingStyle]="endingStyle",e.anchorHidden="data-anchor-hidden",e.side="data-side",e.align="data-align",e})({}),$e=(function(e){return e.popupOpen="data-popup-open",e.pressed="data-pressed",e})({});const kn={[$e.popupOpen]:""},Pn={[$e.popupOpen]:"",[$e.pressed]:""},Fn={[rt.open]:""},Mn={[rt.closed]:""},An={[rt.anchorHidden]:""},hr={open(e){return e?kn:null}},mr={open(e){return e?Pn:null}},br={open(e){return e?Fn:Mn},anchorHidden(e){return e?An:null}};function Er(e){return ze(e,"base-ui")}const Nn="ArrowUp",Ln="ArrowDown",Wn="ArrowLeft",Bn="ArrowRight",Hn="Home",Yn="End",_n=new Set([Wn,Bn]),Dn=new Set([Nn,Ln]),Gn=new Set([..._n,...Dn]),yr=new Set([...Gn,Hn,Yn]),Rr=g.forwardRef(function(t,n){const{cutout:r,...s}=t;let o;if(r){const i=r.getBoundingClientRect();o=`polygon(0% 0%,100% 0%,100% 100%,0% 100%,0% 0%,${i.left}px ${i.top}px,${i.left}px ${i.bottom}px,${i.right}px ${i.bottom}px,${i.right}px ${i.top}px,${i.left}px ${i.top}px)`}return Se.jsx("div",{ref:n,role:"presentation","data-base-ui-inert":"",...s,style:{position:"fixed",inset:0,userSelect:"none",WebkitUserSelect:"none",clipPath:o}})});let Tt={},St={},vt="";function Kn(e){if(typeof document>"u")return!1;const t=U(e);return Be(t).innerWidth-t.documentElement.clientWidth>0}function Un(e){if(!(typeof CSS<"u"&&CSS.supports&&CSS.supports("scrollbar-gutter","stable"))||typeof document>"u")return!1;const n=U(e),r=n.documentElement,s=n.body,o=Ze(r)?r:s,i=o.style.overflowY,u=r.style.scrollbarGutter;r.style.scrollbarGutter="stable",o.style.overflowY="scroll";const h=o.offsetWidth;o.style.overflowY="hidden";const f=o.offsetWidth;return o.style.overflowY=i,r.style.scrollbarGutter=u,h===f}function Vn(e){const t=U(e),n=t.documentElement,r=t.body,s=Ze(n)?n:r,o={overflowY:s.style.overflowY,overflowX:s.style.overflowX};return Object.assign(s.style,{overflowY:"hidden",overflowX:"hidden"}),()=>{Object.assign(s.style,o)}}function Xn(e){const t=U(e),n=t.documentElement,r=t.body,s=Be(n);let o=0,i=0,u=!1;const h=me.create();if(Je&&(s.visualViewport?.scale??1)!==1)return()=>{};function f(){const T=s.getComputedStyle(n),A=s.getComputedStyle(r),y=(T.scrollbarGutter||"").includes("both-edges")?"stable both-edges":"stable";o=n.scrollTop,i=n.scrollLeft,Tt={scrollbarGutter:n.style.scrollbarGutter,overflowY:n.style.overflowY,overflowX:n.style.overflowX},vt=n.style.scrollBehavior,St={position:r.style.position,height:r.style.height,width:r.style.width,boxSizing:r.style.boxSizing,overflowY:r.style.overflowY,overflowX:r.style.overflowX,scrollBehavior:r.style.scrollBehavior};const E=n.scrollHeight>n.clientHeight,Q=n.scrollWidth>n.clientWidth,H=T.overflowY==="scroll"||A.overflowY==="scroll",q=T.overflowX==="scroll"||A.overflowX==="scroll",J=Math.max(0,s.innerWidth-r.clientWidth),D=Math.max(0,s.innerHeight-r.clientHeight),se=parseFloat(A.marginTop)+parseFloat(A.marginBottom),F=parseFloat(A.marginLeft)+parseFloat(A.marginRight),$=Ze(n)?n:r;if(u=Un(e),u){n.style.scrollbarGutter=y,$.style.overflowY="hidden",$.style.overflowX="hidden";return}Object.assign(n.style,{scrollbarGutter:y,overflowY:"hidden",overflowX:"hidden"}),(E||H)&&(n.style.overflowY="scroll"),(Q||q)&&(n.style.overflowX="scroll"),Object.assign(r.style,{position:"relative",height:se||D?`calc(100dvh - ${se+D}px)`:"100dvh",width:F||J?`calc(100vw - ${F+J}px)`:"100vw",boxSizing:"border-box",overflow:"hidden",scrollBehavior:"unset"}),r.scrollTop=o,r.scrollLeft=i,n.setAttribute("data-base-ui-scroll-locked",""),n.style.scrollBehavior="unset"}function k(){Object.assign(n.style,Tt),Object.assign(r.style,St),u||(n.scrollTop=o,n.scrollLeft=i,n.removeAttribute("data-base-ui-scroll-locked"),n.style.scrollBehavior=vt)}function W(){k(),h.request(f)}f();const b=x(s,"resize",W);return()=>{h.cancel(),k(),typeof s.removeEventListener=="function"&&b()}}class qn{lockCount=0;restore=null;timeoutLock=We.create();timeoutUnlock=We.create();acquire(t){return this.lockCount+=1,this.lockCount===1&&this.restore===null&&this.timeoutLock.start(0,()=>this.lock(t)),this.release}release=()=>{this.lockCount-=1,this.lockCount===0&&this.restore&&this.timeoutUnlock.start(0,this.unlock)};unlock=()=>{this.lockCount===0&&this.restore&&(this.restore?.(),this.restore=null)};lock(t){if(this.lockCount===0||this.restore!==null)return;const r=U(t).documentElement,s=Be(r).getComputedStyle(r).overflowY;if(s==="hidden"||s==="clip"){this.restore=wt;return}const o=un||!Kn(t);this.restore=o?Vn(t):Xn(t)}}const $n=new qn;function Tr(e=!0,t=null){Y(()=>{if(e)return $n.acquire(t)},[e,t])}export{wn as A,Ge as B,zn as C,an as D,dn as E,er as F,pn as G,vn as H,Rr as I,Tr as J,rt as K,gr as P,xn as a,tt as b,et as c,Ae as d,cr as e,or as f,Fe as g,ur as h,Jn as i,pr as j,ar as k,fr as l,nr as m,rr as n,dr as o,lr as p,mr as q,Qn as r,ir as s,hr as t,Er as u,Zn as v,sr as w,tr as x,yr as y,br as z};
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
]
|
|
236
236
|
}
|
|
237
237
|
</script>
|
|
238
|
-
<script type="module" crossorigin src="/assets/main-
|
|
238
|
+
<script type="module" crossorigin src="/assets/main-BGYbJv-h.js"></script>
|
|
239
239
|
<link rel="stylesheet" crossorigin href="/assets/main-xFJe2FDa.css">
|
|
240
240
|
</head>
|
|
241
241
|
<body>
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"description": "Shareable Token Tracker dashboard snapshot."
|
|
76
76
|
}
|
|
77
77
|
</script>
|
|
78
|
-
<script type="module" crossorigin src="/assets/main-
|
|
78
|
+
<script type="module" crossorigin src="/assets/main-BGYbJv-h.js"></script>
|
|
79
79
|
<link rel="stylesheet" crossorigin href="/assets/main-xFJe2FDa.css">
|
|
80
80
|
</head>
|
|
81
81
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokentracker-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.60.0",
|
|
4
4
|
"description": "Token usage tracker for AI agent CLIs (Claude Code, Codex, Cursor, Gemini, Antigravity, Kiro, OpenCode, OpenClaw, Every Code, Hermes, GitHub Copilot, Kimi Code, CodeBuddy, WorkBuddy, Grok Build, oh-my-pi, pi, Craft Agents, Kilo CLI, Kilo Code, Roo Code, Zed Agent, Goose, Mimo, ZCode)",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
package/src/cli.js
CHANGED
|
@@ -79,11 +79,11 @@ function printHelp() {
|
|
|
79
79
|
" - --dry-run previews changes without writing files.",
|
|
80
80
|
" - optional: --link-code <code> skips browser login when provided by Dashboard.",
|
|
81
81
|
" - Every Code notify installs when ~/.code/config.toml exists.",
|
|
82
|
-
" - OpenClaw
|
|
82
|
+
" - OpenClaw session plugin auto-links when OpenClaw is installed (requires hooks.allowConversationAccess enabled + gateway restart).",
|
|
83
83
|
" - auto sync waits for a device token.",
|
|
84
84
|
" - optional: --dashboard-url for hosted landing.",
|
|
85
85
|
" - sync parses ~/.codex/sessions/**/rollout-*.jsonl and ~/.code/sessions/**/rollout-*.jsonl, then uploads token deltas.",
|
|
86
|
-
" - --from-openclaw marks sync runs triggered by OpenClaw
|
|
86
|
+
" - --from-openclaw marks sync runs triggered by the OpenClaw session plugin.",
|
|
87
87
|
" - --debug shows original backend errors.",
|
|
88
88
|
" - device-login pairs a headless CLI / SSH session with a browser sign-in (15-min code).",
|
|
89
89
|
"",
|
package/src/commands/status.js
CHANGED
|
@@ -337,6 +337,9 @@ async function cmdStatus(argv = []) {
|
|
|
337
337
|
gemini: geminiHookConfigured,
|
|
338
338
|
opencode_plugin: opencodePluginConfigured,
|
|
339
339
|
openclaw_session_plugin: Boolean(openclawSessionPluginState?.configured),
|
|
340
|
+
openclaw_session_plugin_conversation_access: Boolean(
|
|
341
|
+
openclawSessionPluginState?.conversationAccess,
|
|
342
|
+
),
|
|
340
343
|
openclaw_legacy: Boolean(openclawHookState?.configured),
|
|
341
344
|
codebuddy: codebuddyInstalled ? Boolean(codebuddyHookConfigured) : null,
|
|
342
345
|
workbuddy: workbuddyInstalled ? Boolean(workbuddyHookConfigured) : null,
|
|
@@ -436,6 +439,7 @@ async function cmdStatus(argv = []) {
|
|
|
436
439
|
`- Gemini hooks: ${geminiHookConfigured ? "set" : "unset"}`,
|
|
437
440
|
`- Opencode plugin: ${opencodePluginConfigured ? "set" : "unset"}`,
|
|
438
441
|
`- OpenClaw session plugin: ${openclawSessionPluginState?.configured ? "set" : "unset"}`,
|
|
442
|
+
`- OpenClaw session plugin conversation access: ${openclawSessionPluginState?.conversationAccess ? "set" : "unset"}`,
|
|
439
443
|
`- OpenClaw hook (legacy): ${openclawHookState?.configured ? "set" : "unset"}`,
|
|
440
444
|
kimiInstalled || kimiCodeInstalled
|
|
441
445
|
? `- Kimi Code: passive reader (${kimiWireFiles.length + kimiCodeWireFiles.length} wire.jsonl file${(kimiWireFiles.length + kimiCodeWireFiles.length) !== 1 ? "s" : ""} found)`
|
package/src/commands/sync.js
CHANGED
|
@@ -218,7 +218,7 @@ async function cmdSync(argv) {
|
|
|
218
218
|
const mimoHome = process.env.MIMO_HOME || path.join(xdgDataHome, "mimocode");
|
|
219
219
|
const zcodeHome = process.env.ZCODE_HOME || path.join(home, ".zcode");
|
|
220
220
|
|
|
221
|
-
// OpenClaw
|
|
221
|
+
// OpenClaw session plugin integration: allow the plugin to request incremental parsing for a single session jsonl.
|
|
222
222
|
// We still parse all regular sources so model/source attribution stays complete (e.g. Kimi sessions).
|
|
223
223
|
const openclawSignal = opts.fromOpenclaw
|
|
224
224
|
? resolveOpenclawSignal({ home, env: process.env })
|
|
@@ -294,7 +294,7 @@ async function cmdSync(argv) {
|
|
|
294
294
|
|
|
295
295
|
let openclawResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
296
296
|
if (openclawFiles.length > 0) {
|
|
297
|
-
// Only runs when explicitly triggered by OpenClaw
|
|
297
|
+
// Only runs when explicitly triggered by the OpenClaw session plugin.
|
|
298
298
|
try {
|
|
299
299
|
openclawResult = await parseOpenclawIncremental({
|
|
300
300
|
sessionFiles: openclawFiles,
|
package/src/lib/diagnostics.js
CHANGED
|
@@ -190,6 +190,9 @@ async function collectTrackerDiagnostics({
|
|
|
190
190
|
openclaw_session_plugin_configured: Boolean(openclawSessionPluginState?.configured),
|
|
191
191
|
openclaw_session_plugin_linked: Boolean(openclawSessionPluginState?.linked),
|
|
192
192
|
openclaw_session_plugin_enabled: Boolean(openclawSessionPluginState?.enabled),
|
|
193
|
+
openclaw_session_plugin_conversation_access: Boolean(
|
|
194
|
+
openclawSessionPluginState?.conversationAccess,
|
|
195
|
+
),
|
|
193
196
|
openclaw_hook_configured: Boolean(openclawHookState?.configured),
|
|
194
197
|
openclaw_hook_linked: Boolean(openclawHookState?.linked),
|
|
195
198
|
openclaw_hook_enabled: Boolean(openclawHookState?.enabled),
|
|
@@ -67,13 +67,15 @@ async function installOpenclawSessionPlugin({
|
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
const policyResult = await ensureOpenclawSessionPluginPolicy(paths);
|
|
70
71
|
const state = await probeOpenclawSessionPluginState({ home, trackerDir, env });
|
|
71
72
|
return {
|
|
72
73
|
configured: state.configured,
|
|
73
74
|
changed:
|
|
74
75
|
/Linked plugin path:/i.test(installResult.stdout || "") ||
|
|
75
76
|
/Enabled plugin/i.test(enableResult.stdout || "") ||
|
|
76
|
-
/already enabled/i.test(enableResult.stdout || "")
|
|
77
|
+
/already enabled/i.test(enableResult.stdout || "") ||
|
|
78
|
+
policyResult.changed,
|
|
77
79
|
...paths,
|
|
78
80
|
stdout: `${installResult.stdout || ""}\n${enableResult.stdout || ""}`.trim(),
|
|
79
81
|
stderr: `${installResult.stderr || ""}\n${enableResult.stderr || ""}`.trim(),
|
|
@@ -151,6 +153,7 @@ async function probeOpenclawSessionPluginState({
|
|
|
151
153
|
|
|
152
154
|
const pluginEntry = cfg?.plugins?.entries?.[pluginId];
|
|
153
155
|
const enabled = pluginEntry ? pluginEntry.enabled !== false : false;
|
|
156
|
+
const conversationAccess = pluginEntry?.hooks?.allowConversationAccess === true;
|
|
154
157
|
|
|
155
158
|
const loadPaths = Array.isArray(cfg?.plugins?.load?.paths) ? cfg.plugins.load.paths : [];
|
|
156
159
|
const normalizedPluginEntryDir = path.resolve(pluginEntryDir);
|
|
@@ -164,15 +167,64 @@ async function probeOpenclawSessionPluginState({
|
|
|
164
167
|
const installed = Boolean(installEntry);
|
|
165
168
|
|
|
166
169
|
return {
|
|
167
|
-
configured: enabled && linked && pluginFilesReady,
|
|
170
|
+
configured: enabled && linked && pluginFilesReady && conversationAccess,
|
|
168
171
|
enabled,
|
|
169
172
|
linked,
|
|
170
173
|
installed,
|
|
171
174
|
pluginFilesReady,
|
|
175
|
+
conversationAccess,
|
|
172
176
|
...paths,
|
|
173
177
|
};
|
|
174
178
|
}
|
|
175
179
|
|
|
180
|
+
async function ensureOpenclawSessionPluginPolicy({ openclawConfigPath, pluginId } = {}) {
|
|
181
|
+
if (!openclawConfigPath || !pluginId) {
|
|
182
|
+
throw new Error("openclawConfigPath and pluginId are required");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let cfg = JSON.parse(await fs.readFile(openclawConfigPath, "utf8"));
|
|
186
|
+
if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) {
|
|
187
|
+
cfg = {};
|
|
188
|
+
}
|
|
189
|
+
if (!cfg.plugins || typeof cfg.plugins !== "object" || Array.isArray(cfg.plugins)) {
|
|
190
|
+
cfg.plugins = {};
|
|
191
|
+
}
|
|
192
|
+
if (
|
|
193
|
+
!cfg.plugins.entries ||
|
|
194
|
+
typeof cfg.plugins.entries !== "object" ||
|
|
195
|
+
Array.isArray(cfg.plugins.entries)
|
|
196
|
+
) {
|
|
197
|
+
cfg.plugins.entries = {};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const existingEntry = cfg.plugins.entries[pluginId];
|
|
201
|
+
const entry =
|
|
202
|
+
existingEntry && typeof existingEntry === "object" && !Array.isArray(existingEntry)
|
|
203
|
+
? existingEntry
|
|
204
|
+
: {};
|
|
205
|
+
cfg.plugins.entries[pluginId] = entry;
|
|
206
|
+
|
|
207
|
+
let changed = existingEntry !== entry;
|
|
208
|
+
if (entry.enabled === false) {
|
|
209
|
+
entry.enabled = true;
|
|
210
|
+
changed = true;
|
|
211
|
+
}
|
|
212
|
+
if (!entry.hooks || typeof entry.hooks !== "object" || Array.isArray(entry.hooks)) {
|
|
213
|
+
entry.hooks = {};
|
|
214
|
+
changed = true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (entry.hooks.allowConversationAccess === true) {
|
|
218
|
+
if (!changed) return { changed: false };
|
|
219
|
+
} else {
|
|
220
|
+
entry.hooks.allowConversationAccess = true;
|
|
221
|
+
changed = true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
await fs.writeFile(openclawConfigPath, `${JSON.stringify(cfg, null, 2)}\n`, "utf8");
|
|
225
|
+
return { changed: true };
|
|
226
|
+
}
|
|
227
|
+
|
|
176
228
|
async function removeOpenclawSessionPluginConfig({
|
|
177
229
|
home = os.homedir(),
|
|
178
230
|
trackerDir,
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// OpenCode Go usage limits.
|
|
2
|
+
//
|
|
3
|
+
// Scrapes the OpenCode Go workspace dashboard
|
|
4
|
+
// (https://opencode.ai/workspace/<id>/go) for rolling (5h) / weekly / monthly
|
|
5
|
+
// usage. The opencode web console has no public REST API for quota today
|
|
6
|
+
// (tracked at anomalyco/opencode#16017, anomalyco/opencode#16513), so we
|
|
7
|
+
// read the same data the React app uses by parsing the SSR hydration output
|
|
8
|
+
// (SolidStart `queryLiteSubscription` serializes the result as
|
|
9
|
+
// `rollingUsage:$R[N]={...usagePercent:N...resetInSec:N...}`) with a
|
|
10
|
+
// `data-slot="usage-item"` HTML fallback.
|
|
11
|
+
//
|
|
12
|
+
// Approach ported from slkiser/opencode-quota PR #41 (MIT, Apr 12 2026,
|
|
13
|
+
// 430 tests passing) — that project independently arrived at the same
|
|
14
|
+
// scrape with the same env-var names. The cookie is sent verbatim as
|
|
15
|
+
// `Cookie: auth=<OPENCODE_GO_AUTH_COOKIE>` per that reference.
|
|
16
|
+
|
|
17
|
+
const SCRAPED_NUMBER_PATTERN = "(-?\\d+(?:\\.\\d+)?)";
|
|
18
|
+
|
|
19
|
+
const RE_ROLLING_PCT_FIRST = new RegExp(
|
|
20
|
+
`rollingUsage:\\$R\\[\\d+\\]=\\{[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*\\}`,
|
|
21
|
+
);
|
|
22
|
+
const RE_ROLLING_RESET_FIRST = new RegExp(
|
|
23
|
+
`rollingUsage:\\$R\\[\\d+\\]=\\{[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*\\}`,
|
|
24
|
+
);
|
|
25
|
+
const RE_WEEKLY_PCT_FIRST = new RegExp(
|
|
26
|
+
`weeklyUsage:\\$R\\[\\d+\\]=\\{[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*\\}`,
|
|
27
|
+
);
|
|
28
|
+
const RE_WEEKLY_RESET_FIRST = new RegExp(
|
|
29
|
+
`weeklyUsage:\\$R\\[\\d+\\]=\\{[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*\\}`,
|
|
30
|
+
);
|
|
31
|
+
const RE_MONTHLY_PCT_FIRST = new RegExp(
|
|
32
|
+
`monthlyUsage:\\$R\\[\\d+\\]=\\{[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*\\}`,
|
|
33
|
+
);
|
|
34
|
+
const RE_MONTHLY_RESET_FIRST = new RegExp(
|
|
35
|
+
`monthlyUsage:\\$R\\[\\d+\\]=\\{[^}]*resetInSec:${SCRAPED_NUMBER_PATTERN}[^}]*usagePercent:${SCRAPED_NUMBER_PATTERN}[^}]*\\}`,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const DASHBOARD_URL_PREFIX = "https://opencode.ai/workspace/";
|
|
39
|
+
const DASHBOARD_URL_SUFFIX = "/go";
|
|
40
|
+
const USER_AGENT =
|
|
41
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Gecko/20100101 Firefox/148.0";
|
|
42
|
+
const DEFAULT_SCRAPE_TIMEOUT_MS = 10_000;
|
|
43
|
+
|
|
44
|
+
function readConfig(env = process.env) {
|
|
45
|
+
if (!env || typeof env !== "object") return null;
|
|
46
|
+
const workspaceId =
|
|
47
|
+
typeof env.OPENCODE_GO_WORKSPACE_ID === "string"
|
|
48
|
+
? env.OPENCODE_GO_WORKSPACE_ID.trim()
|
|
49
|
+
: "";
|
|
50
|
+
const authCookie =
|
|
51
|
+
typeof env.OPENCODE_GO_AUTH_COOKIE === "string"
|
|
52
|
+
? env.OPENCODE_GO_AUTH_COOKIE.trim()
|
|
53
|
+
: "";
|
|
54
|
+
if (!workspaceId || !authCookie) return null;
|
|
55
|
+
return { workspaceId, authCookie };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function clampPercent(value) {
|
|
59
|
+
if (value === null || value === undefined || value === "") return null;
|
|
60
|
+
const n = Number(value);
|
|
61
|
+
if (!Number.isFinite(n)) return null;
|
|
62
|
+
if (n <= 0) return 0;
|
|
63
|
+
if (n >= 100) return 100;
|
|
64
|
+
return n;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function buildWindow({ usagePercent, resetInSec, nowMs }) {
|
|
68
|
+
const pct = clampPercent(usagePercent);
|
|
69
|
+
if (pct === null) return null;
|
|
70
|
+
const seconds = Number(resetInSec);
|
|
71
|
+
if (!Number.isFinite(seconds) || seconds < 0) return null;
|
|
72
|
+
const resetAtIso = new Date(nowMs + Math.floor(seconds) * 1000).toISOString();
|
|
73
|
+
return { used_percent: pct, reset_at: resetAtIso };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function parseWindowUsage(html, rePctFirst, reResetFirst) {
|
|
77
|
+
const pctFirst = rePctFirst.exec(html);
|
|
78
|
+
if (pctFirst) {
|
|
79
|
+
const usagePercent = Number(pctFirst[1]);
|
|
80
|
+
const resetInSec = Number(pctFirst[2]);
|
|
81
|
+
if (Number.isFinite(usagePercent) && Number.isFinite(resetInSec)) {
|
|
82
|
+
return { usagePercent, resetInSec };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const resetFirst = reResetFirst.exec(html);
|
|
86
|
+
if (resetFirst) {
|
|
87
|
+
const resetInSec = Number(resetFirst[1]);
|
|
88
|
+
const usagePercent = Number(resetFirst[2]);
|
|
89
|
+
if (Number.isFinite(usagePercent) && Number.isFinite(resetInSec)) {
|
|
90
|
+
return { usagePercent, resetInSec };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Parse "1 hour 56 minutes" / "6 days 2 hours" / "26 days 17 hours" into
|
|
97
|
+
// seconds. The data-slot HTML fallback uses human-readable reset strings
|
|
98
|
+
// when the SSR hydration output is absent.
|
|
99
|
+
function parseHumanReadableTime(timeStr) {
|
|
100
|
+
if (typeof timeStr !== "string") return null;
|
|
101
|
+
const normalized = timeStr.toLowerCase().trim().replace(/\s+/g, " ");
|
|
102
|
+
if (["reset-now", "reset now", "now", "resets now"].includes(normalized)) {
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
const dayMatch = normalized.match(/(\d+(?:\.\d+)?)\s*days?/);
|
|
106
|
+
const hourMatch = normalized.match(/(\d+(?:\.\d+)?)\s*hours?/);
|
|
107
|
+
const minuteMatch = normalized.match(/(\d+(?:\.\d+)?)\s*minutes?/);
|
|
108
|
+
const secondMatch = normalized.match(/(\d+(?:\.\d+)?)\s*seconds?/);
|
|
109
|
+
if (!dayMatch && !hourMatch && !minuteMatch && !secondMatch) return null;
|
|
110
|
+
let total = 0;
|
|
111
|
+
if (dayMatch) total += Number(dayMatch[1]) * 86400;
|
|
112
|
+
if (hourMatch) total += Number(hourMatch[1]) * 3600;
|
|
113
|
+
if (minuteMatch) total += Number(minuteMatch[1]) * 60;
|
|
114
|
+
if (secondMatch) total += Number(secondMatch[1]);
|
|
115
|
+
return total;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function parseDataSlotFormat(html) {
|
|
119
|
+
const out = {};
|
|
120
|
+
const items = html.split(/data-slot="usage-item"/);
|
|
121
|
+
for (let i = 1; i < items.length; i++) {
|
|
122
|
+
const content = items[i];
|
|
123
|
+
const labelMatch = content.match(/data-slot="usage-label">([^<]+)</);
|
|
124
|
+
if (!labelMatch) continue;
|
|
125
|
+
const label = labelMatch[1].trim().toLowerCase();
|
|
126
|
+
const usageMatch = content.match(/data-slot="usage-value">[^0-9]*(\d+(?:\.\d+)?)/);
|
|
127
|
+
if (!usageMatch) continue;
|
|
128
|
+
const usagePercent = Number(usageMatch[1]);
|
|
129
|
+
const resetMatch = content.match(
|
|
130
|
+
/data-slot="(reset-time|reset-now)">([\s\S]*?)<\/span>/,
|
|
131
|
+
);
|
|
132
|
+
if (!resetMatch) continue;
|
|
133
|
+
const resetContent = resetMatch[2]
|
|
134
|
+
.replace(/<!--[\s\S]*?-->/g, "")
|
|
135
|
+
.replace(/Resets?\s*in\s*/i, "")
|
|
136
|
+
.trim();
|
|
137
|
+
const resetInSec =
|
|
138
|
+
resetMatch[1] === "reset-now" ? 0 : parseHumanReadableTime(resetContent);
|
|
139
|
+
if (!Number.isFinite(usagePercent) || resetInSec === null) continue;
|
|
140
|
+
let key = null;
|
|
141
|
+
if (label.includes("rolling")) key = "rolling";
|
|
142
|
+
else if (label.includes("weekly")) key = "weekly";
|
|
143
|
+
else if (label.includes("monthly")) key = "monthly";
|
|
144
|
+
if (key) out[key] = { usagePercent, resetInSec };
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function extractWindows(html, nowMs) {
|
|
150
|
+
let rolling = parseWindowUsage(html, RE_ROLLING_PCT_FIRST, RE_ROLLING_RESET_FIRST);
|
|
151
|
+
let weekly = parseWindowUsage(html, RE_WEEKLY_PCT_FIRST, RE_WEEKLY_RESET_FIRST);
|
|
152
|
+
let monthly = parseWindowUsage(html, RE_MONTHLY_PCT_FIRST, RE_MONTHLY_RESET_FIRST);
|
|
153
|
+
// Fill any *individual* missing window from the data-slot HTML fallback.
|
|
154
|
+
// Running the fallback only when all three fail loses the case where SSR
|
|
155
|
+
// hydration exposes e.g. rollingUsage but drops weeklyUsage — we'd return
|
|
156
|
+
// `null` for that window even though parseDataSlotFormat() could still
|
|
157
|
+
// recover it from the rendered HTML.
|
|
158
|
+
if (!rolling || !weekly || !monthly) {
|
|
159
|
+
const fallback = parseDataSlotFormat(html);
|
|
160
|
+
rolling = rolling || fallback.rolling || null;
|
|
161
|
+
weekly = weekly || fallback.weekly || null;
|
|
162
|
+
monthly = monthly || fallback.monthly || null;
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
rolling: rolling ? buildWindow({ ...rolling, nowMs }) : null,
|
|
166
|
+
weekly: weekly ? buildWindow({ ...weekly, nowMs }) : null,
|
|
167
|
+
monthly: monthly ? buildWindow({ ...monthly, nowMs }) : null,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function sanitizeMessage(text, maxLength = 160) {
|
|
172
|
+
const str = typeof text === "string" ? text : String(text ?? "");
|
|
173
|
+
const squashed = str.replace(/\s+/g, " ").trim();
|
|
174
|
+
return (squashed || "unknown").slice(0, maxLength);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function withTimeout(fetchImpl, timeoutMs) {
|
|
178
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return fetchImpl;
|
|
179
|
+
return (input, init = {}) => {
|
|
180
|
+
const controller = new AbortController();
|
|
181
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
182
|
+
const next = { ...init, signal: init.signal || controller.signal };
|
|
183
|
+
return fetchImpl(input, next).finally(() => clearTimeout(timer));
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function fetchOpencodeGoLimits({
|
|
188
|
+
env = process.env,
|
|
189
|
+
fetchImpl = fetch,
|
|
190
|
+
nowMs = Date.now(),
|
|
191
|
+
timeoutMs = DEFAULT_SCRAPE_TIMEOUT_MS,
|
|
192
|
+
} = {}) {
|
|
193
|
+
const cfg = readConfig(env);
|
|
194
|
+
if (!cfg) return { configured: false };
|
|
195
|
+
|
|
196
|
+
const url =
|
|
197
|
+
DASHBOARD_URL_PREFIX + encodeURIComponent(cfg.workspaceId) + DASHBOARD_URL_SUFFIX;
|
|
198
|
+
|
|
199
|
+
let response;
|
|
200
|
+
try {
|
|
201
|
+
response = await withTimeout(fetchImpl, timeoutMs)(url, {
|
|
202
|
+
method: "GET",
|
|
203
|
+
headers: {
|
|
204
|
+
"User-Agent": USER_AGENT,
|
|
205
|
+
Accept: "text/html",
|
|
206
|
+
Cookie: `auth=${cfg.authCookie}`,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
} catch (err) {
|
|
210
|
+
return { configured: true, error: sanitizeMessage(err?.message || err) };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (response.status === 401 || response.status === 403) {
|
|
214
|
+
return {
|
|
215
|
+
configured: true,
|
|
216
|
+
error: "Not signed in to OpenCode Go. Refresh the auth cookie in OPENCODE_GO_AUTH_COOKIE.",
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
if (!response.ok) {
|
|
220
|
+
return {
|
|
221
|
+
configured: true,
|
|
222
|
+
error: `OpenCode Go dashboard error ${response.status}`,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let html;
|
|
227
|
+
try {
|
|
228
|
+
html = await response.text();
|
|
229
|
+
} catch (err) {
|
|
230
|
+
return { configured: true, error: sanitizeMessage(err?.message || err) };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const { rolling, weekly, monthly } = extractWindows(html, nowMs);
|
|
234
|
+
if (!rolling && !weekly && !monthly) {
|
|
235
|
+
return {
|
|
236
|
+
configured: true,
|
|
237
|
+
error:
|
|
238
|
+
"Could not parse any known OpenCode Go dashboard usage windows (rollingUsage, weeklyUsage, monthlyUsage). The page layout may have changed.",
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
configured: true,
|
|
244
|
+
error: null,
|
|
245
|
+
// No `plan_label` — the brand name "OpenCode Go" is the row title, so
|
|
246
|
+
// appending "Go" again would render "OpenCode Go Go" in the panel.
|
|
247
|
+
primary_window: rolling,
|
|
248
|
+
secondary_window: weekly,
|
|
249
|
+
tertiary_window: monthly,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
module.exports = {
|
|
254
|
+
fetchOpencodeGoLimits,
|
|
255
|
+
readConfig,
|
|
256
|
+
extractWindows,
|
|
257
|
+
parseWindowUsage,
|
|
258
|
+
parseDataSlotFormat,
|
|
259
|
+
parseHumanReadableTime,
|
|
260
|
+
buildWindow,
|
|
261
|
+
};
|