tokentracker-cli 0.24.5 → 0.24.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dashboard/dist/assets/{Card-Dmmj336P.js → Card-BfayTmBt.js} +1 -1
  2. package/dashboard/dist/assets/{DashboardPage-BHYKJEV6.js → DashboardPage-C_ExwqoB.js} +1 -1
  3. package/dashboard/dist/assets/{DevicePage-DwsKn4Ov.js → DevicePage-Bzc-tOQ7.js} +1 -1
  4. package/dashboard/dist/assets/{FadeIn-B8VmShpI.js → FadeIn-C1nCEQAI.js} +1 -1
  5. package/dashboard/dist/assets/{HeaderGithubStar-B_KecfbF.js → HeaderGithubStar-CalgbIws.js} +1 -1
  6. package/dashboard/dist/assets/{IpCheckPage-BgG1wCTr.js → IpCheckPage-C2_FdWEa.js} +1 -1
  7. package/dashboard/dist/assets/{LandingPage-ql_hYK8I.js → LandingPage-Cab2gSJk.js} +1 -1
  8. package/dashboard/dist/assets/{LeaderboardPage-D_jXlgYG.js → LeaderboardPage-BoWSvv8E.js} +1 -1
  9. package/dashboard/dist/assets/{LeaderboardProfilePage-DFUaU6I1.js → LeaderboardProfilePage-BxvvRB0p.js} +1 -1
  10. package/dashboard/dist/assets/{LimitsPage-mQdxcN_z.js → LimitsPage-JTd7ZYkv.js} +1 -1
  11. package/dashboard/dist/assets/{LoginPage-D1lzth57.js → LoginPage-fU320alu.js} +1 -1
  12. package/dashboard/dist/assets/{PopoverPopup-DIB4uXcd.js → PopoverPopup-C76-ba7G.js} +1 -1
  13. package/dashboard/dist/assets/{ProviderIcon-CFDBvFyw.js → ProviderIcon-xv4cUgTy.js} +1 -1
  14. package/dashboard/dist/assets/{SettingsPage-BN1OqYbm.js → SettingsPage-UtWH1_mF.js} +1 -1
  15. package/dashboard/dist/assets/{SkillsPage-wBzXeval.js → SkillsPage-9W2jGGps.js} +1 -1
  16. package/dashboard/dist/assets/{WidgetsPage-DhZSen2-.js → WidgetsPage-BedujTKv.js} +1 -1
  17. package/dashboard/dist/assets/{WrappedPage-DhDlrrUL.js → WrappedPage-Bms62oTH.js} +1 -1
  18. package/dashboard/dist/assets/check-DHKWR9eH.js +1 -0
  19. package/dashboard/dist/assets/{chevron-down-bXSVV-pK.js → chevron-down-g6db-hJJ.js} +1 -1
  20. package/dashboard/dist/assets/{download-DvJmehef.js → download-UDqrzLfH.js} +1 -1
  21. package/dashboard/dist/assets/{info-lgmn3oZc.js → info-BB9X3uhm.js} +1 -1
  22. package/dashboard/dist/assets/{leaderboard-columns-Bbx3-Y6N.js → leaderboard-columns-CTGzd-uH.js} +1 -1
  23. package/dashboard/dist/assets/{main-DVwx7yxG.js → main-QPJFCBQm.js} +2 -2
  24. package/dashboard/dist/assets/{use-limits-display-prefs-CeHiE8M0.js → use-limits-display-prefs-DccYvUNZ.js} +1 -1
  25. package/dashboard/dist/assets/{use-native-settings-iXw-cXKZ.js → use-native-settings-DAbSUv-n.js} +1 -1
  26. package/dashboard/dist/assets/{use-reduced-motion-BWHrwYlb.js → use-reduced-motion-B_GaKtWC.js} +1 -1
  27. package/dashboard/dist/assets/{use-usage-limits-DPaSUkzX.js → use-usage-limits-3pTcFcoF.js} +1 -1
  28. package/dashboard/dist/assets/{useCurrency-DqvOkq1e.js → useCurrency-CLZ2MqvV.js} +1 -1
  29. package/dashboard/dist/index.html +1 -1
  30. package/dashboard/dist/share.html +1 -1
  31. package/package.json +1 -1
  32. package/src/lib/local-api.js +66 -19
  33. package/src/lib/pricing/seed-snapshot.json +1 -1
  34. package/dashboard/dist/assets/check-MvbdiVTJ.js +0 -1
@@ -1 +1 @@
1
- import{r as a}from"./main-DVwx7yxG.js";const d=["claude","codex","cursor","gemini","kimi","kiro","copilot","antigravity"],S={claude:"Claude",codex:"Codex",cursor:"Cursor",gemini:"Gemini",kimi:"Kimi",kiro:"Kiro",copilot:"GitHub Copilot",antigravity:"Antigravity"},v={claude:"/brand-logos/claude-code.svg",codex:"/brand-logos/codex.svg",cursor:"/brand-logos/cursor.svg",gemini:"/brand-logos/gemini.svg",kimi:"/brand-logos/kimi.svg",kiro:"/brand-logos/kiro.svg",copilot:"/brand-logos/copilot.svg",antigravity:"/brand-logos/antigravity.svg"},l="tt.limits.providerOrder",g="tt.limits.providerVisibility";function w(){if(typeof window>"u")return[...d];try{const i=window.localStorage.getItem(l);if(!i)return[...d];const s=JSON.parse(i);if(!Array.isArray(s))return[...d];const r=s.filter(c=>d.includes(c));for(const c of d)r.includes(c)||r.push(c);return r}catch{return[...d]}}function y(){const i=Object.fromEntries(d.map(s=>[s,!0]));if(typeof window>"u")return i;try{const s=window.localStorage.getItem(g);if(!s)return i;const r=JSON.parse(s);if(!r||typeof r!="object")return i;const c={...i};for(const u of d)typeof r[u]=="boolean"&&(c[u]=r[u]);return c}catch{return i}}function C(){const[i,s]=a.useState(w),[r,c]=a.useState(y);a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(l,JSON.stringify(i))}catch{}},[i]),a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(g,JSON.stringify(r))}catch{}},[r]),a.useEffect(()=>{if(typeof window>"u")return;const o=t=>{t.key===l&&s(w()),t.key===g&&c(y())};return window.addEventListener("storage",o),()=>window.removeEventListener("storage",o)},[]);const u=a.useCallback(o=>{c(t=>({...t,[o]:!t[o]}))},[]),b=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<=0)return t;const n=[...t];return[n[e-1],n[e]]=[n[e],n[e-1]],n})},[]),p=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<0||e>=t.length-1)return t;const n=[...t];return[n[e],n[e+1]]=[n[e+1],n[e]],n})},[]),O=a.useCallback((o,t)=>{o!==t&&s(e=>{const n=e.indexOf(o),m=e.indexOf(t);if(n<0||m<0)return e;const f=[...e],[I]=f.splice(n,1);return f.splice(m,0,I),f})},[]),k=a.useCallback(()=>{s([...d]),c(Object.fromEntries(d.map(o=>[o,!0])))},[]),x=a.useMemo(()=>i.filter(o=>r[o]!==!1),[i,r]);return{order:i,visibility:r,visibleOrdered:x,toggle:u,moveUp:b,moveDown:p,moveToward:O,reset:k}}export{v as L,S as a,C as u};
1
+ import{r as a}from"./main-QPJFCBQm.js";const d=["claude","codex","cursor","gemini","kimi","kiro","copilot","antigravity"],S={claude:"Claude",codex:"Codex",cursor:"Cursor",gemini:"Gemini",kimi:"Kimi",kiro:"Kiro",copilot:"GitHub Copilot",antigravity:"Antigravity"},v={claude:"/brand-logos/claude-code.svg",codex:"/brand-logos/codex.svg",cursor:"/brand-logos/cursor.svg",gemini:"/brand-logos/gemini.svg",kimi:"/brand-logos/kimi.svg",kiro:"/brand-logos/kiro.svg",copilot:"/brand-logos/copilot.svg",antigravity:"/brand-logos/antigravity.svg"},l="tt.limits.providerOrder",g="tt.limits.providerVisibility";function w(){if(typeof window>"u")return[...d];try{const i=window.localStorage.getItem(l);if(!i)return[...d];const s=JSON.parse(i);if(!Array.isArray(s))return[...d];const r=s.filter(c=>d.includes(c));for(const c of d)r.includes(c)||r.push(c);return r}catch{return[...d]}}function y(){const i=Object.fromEntries(d.map(s=>[s,!0]));if(typeof window>"u")return i;try{const s=window.localStorage.getItem(g);if(!s)return i;const r=JSON.parse(s);if(!r||typeof r!="object")return i;const c={...i};for(const u of d)typeof r[u]=="boolean"&&(c[u]=r[u]);return c}catch{return i}}function C(){const[i,s]=a.useState(w),[r,c]=a.useState(y);a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(l,JSON.stringify(i))}catch{}},[i]),a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(g,JSON.stringify(r))}catch{}},[r]),a.useEffect(()=>{if(typeof window>"u")return;const o=t=>{t.key===l&&s(w()),t.key===g&&c(y())};return window.addEventListener("storage",o),()=>window.removeEventListener("storage",o)},[]);const u=a.useCallback(o=>{c(t=>({...t,[o]:!t[o]}))},[]),b=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<=0)return t;const n=[...t];return[n[e-1],n[e]]=[n[e],n[e-1]],n})},[]),p=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<0||e>=t.length-1)return t;const n=[...t];return[n[e],n[e+1]]=[n[e+1],n[e]],n})},[]),O=a.useCallback((o,t)=>{o!==t&&s(e=>{const n=e.indexOf(o),m=e.indexOf(t);if(n<0||m<0)return e;const f=[...e],[I]=f.splice(n,1);return f.splice(m,0,I),f})},[]),k=a.useCallback(()=>{s([...d]),c(Object.fromEntries(d.map(o=>[o,!0])))},[]),x=a.useMemo(()=>i.filter(o=>r[o]!==!1),[i,r]);return{order:i,visibility:r,visibleOrdered:x,toggle:u,moveUp:b,moveDown:p,moveToward:O,reset:k}}export{v as L,S as a,C as u};
@@ -1 +1 @@
1
- import{C as a,a7 as x,r as n,aH as g,aI as m,aJ as b,aK as u,aL as f,aF as h}from"./main-DVwx7yxG.js";import{C as y}from"./Card-Dmmj336P.js";function p({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 j({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,p as T,j as a,w as b,S as u};
1
+ import{C as a,a7 as x,r as n,aH as g,aI as m,aJ as b,aK as u,aL as f,aF as h}from"./main-QPJFCBQm.js";import{C as y}from"./Card-BfayTmBt.js";function p({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 j({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,p as T,j as a,w as b,S as u};
@@ -1 +1 @@
1
- import{aM as t,aN as o,r,aO as s}from"./main-DVwx7yxG.js";function u(){!t.current&&o();const[e]=r.useState(s.current);return e}export{u};
1
+ import{aM as t,aN as o,r,aO as s}from"./main-QPJFCBQm.js";function u(){!t.current&&o();const[e]=r.useState(s.current);return e}export{u};
@@ -1 +1 @@
1
- import{r,al as l}from"./main-DVwx7yxG.js";function y(i){const[o,a]=r.useState(null),[u,s]=r.useState(null),[c,f]=r.useState(!0),n=!!i?.initialRefresh,g=r.useCallback(async()=>{try{const e=await l({refresh:!0});a(e&&typeof e=="object"?e:null),s(null)}catch(e){s(e?.message||String(e))}},[]);return r.useEffect(()=>{let e=!1;return(async()=>{try{const t=await l(n?{refresh:!0}:{});if(e)return;a(t&&typeof t=="object"?t:null),s(null)}catch(t){if(e)return;s(t?.message||String(t))}finally{e||f(!1)}})(),()=>{e=!0}},[n]),{data:o,error:u,isLoading:c,refresh:g}}export{y as u};
1
+ import{r,al as l}from"./main-QPJFCBQm.js";function y(i){const[o,a]=r.useState(null),[u,s]=r.useState(null),[c,f]=r.useState(!0),n=!!i?.initialRefresh,g=r.useCallback(async()=>{try{const e=await l({refresh:!0});a(e&&typeof e=="object"?e:null),s(null)}catch(e){s(e?.message||String(e))}},[]);return r.useEffect(()=>{let e=!1;return(async()=>{try{const t=await l(n?{refresh:!0}:{});if(e)return;a(t&&typeof t=="object"?t:null),s(null)}catch(t){if(e)return;s(t?.message||String(t))}finally{e||f(!1)}})(),()=>{e=!0}},[n]),{data:o,error:u,isLoading:c,refresh:g}}export{y as u};
@@ -1 +1 @@
1
- import{r as e,aA as t,aB as r,I as c}from"./main-DVwx7yxG.js";const a=Object.freeze({currency:c,rate:1,symbol:"$",rates:{...r},rateSource:"default",rateFetchedAt:null,setCurrency:()=>{}});function u(){return e.useContext(t)??a}export{u};
1
+ import{r as e,aA as t,aB as r,I as c}from"./main-QPJFCBQm.js";const a=Object.freeze({currency:c,rate:1,symbol:"$",rates:{...r},rateSource:"default",rateFetchedAt:null,setCurrency:()=>{}});function u(){return e.useContext(t)??a}export{u};
@@ -210,7 +210,7 @@
210
210
  ]
211
211
  }
212
212
  </script>
213
- <script type="module" crossorigin src="/assets/main-DVwx7yxG.js"></script>
213
+ <script type="module" crossorigin src="/assets/main-QPJFCBQm.js"></script>
214
214
  <link rel="stylesheet" crossorigin href="/assets/main-C8k06i2w.css">
215
215
  </head>
216
216
  <body>
@@ -51,7 +51,7 @@
51
51
  "description": "Shareable Token Tracker dashboard snapshot."
52
52
  }
53
53
  </script>
54
- <script type="module" crossorigin src="/assets/main-DVwx7yxG.js"></script>
54
+ <script type="module" crossorigin src="/assets/main-QPJFCBQm.js"></script>
55
55
  <link rel="stylesheet" crossorigin href="/assets/main-C8k06i2w.css">
56
56
  </head>
57
57
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokentracker-cli",
3
- "version": "0.24.5",
3
+ "version": "0.24.6",
4
4
  "description": "Token usage tracker for AI agent CLIs (Claude Code, Codex, Cursor, Gemini, Kiro, OpenCode, OpenClaw, Every Code, Hermes, GitHub Copilot, Kimi Code, CodeBuddy, Grok Build, oh-my-pi, pi, Craft Agents, Kilo CLI, Kilo Code, Roo Code, Zed Agent, Goose)",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
@@ -653,6 +653,67 @@ function json(res, data, status) {
653
653
  const IP_CHECK_PROXY_PREFIX = "/proxy/ipcheck";
654
654
  const IP_CHECK_TARGET = "https://ip.net.coffee";
655
655
 
656
+ // HTTP hop-by-hop headers (RFC 7230 §6.1) plus headers undici/fetch manages
657
+ // internally. Forwarding any of these to `fetch(...)` either silently breaks
658
+ // the request (host being wrong) or, on stricter undici versions like the
659
+ // 6.24.1 shipped with Node 22.22.2, throws UND_ERR_INVALID_ARG and turns
660
+ // every proxied POST into a 502. Keep this set authoritative for every
661
+ // reverse-proxy site in this module.
662
+ const HOP_BY_HOP_HEADERS = new Set([
663
+ "host",
664
+ "connection",
665
+ "keep-alive",
666
+ "content-length",
667
+ "transfer-encoding",
668
+ "upgrade",
669
+ "proxy-authorization",
670
+ "proxy-authenticate",
671
+ "proxy-connection",
672
+ "te",
673
+ "trailer",
674
+ "trailers",
675
+ ]);
676
+
677
+ // Strip forbidden + hop-by-hop headers when forwarding an inbound request to
678
+ // fetch(). Honours the Connection header's named-headers list (RFC 7230 §6.1)
679
+ // so values like `Connection: keep-alive, x-custom` also drop x-custom.
680
+ function buildProxyHeaders(headers) {
681
+ const entries =
682
+ headers && typeof headers.entries === "function"
683
+ ? Array.from(headers.entries())
684
+ : Object.entries(headers || {});
685
+
686
+ const connectionNamed = new Set();
687
+ const normalized = [];
688
+ for (const [rawKey, rawValue] of entries) {
689
+ if (rawValue == null) continue;
690
+ const key = String(rawKey).toLowerCase();
691
+ normalized.push([key, rawValue]);
692
+ if (key === "connection") {
693
+ const values = Array.isArray(rawValue) ? rawValue : [rawValue];
694
+ for (const v of values) {
695
+ String(v)
696
+ .split(",")
697
+ .map((part) => part.trim().toLowerCase())
698
+ .filter(Boolean)
699
+ .forEach((part) => connectionNamed.add(part));
700
+ }
701
+ }
702
+ }
703
+
704
+ const out = {};
705
+ for (const [key, rawValue] of normalized) {
706
+ if (HOP_BY_HOP_HEADERS.has(key) || connectionNamed.has(key)) continue;
707
+ if (Array.isArray(rawValue)) {
708
+ const joined = rawValue.filter((e) => e != null).map(String).join(", ");
709
+ if (joined) out[key] = joined;
710
+ continue;
711
+ }
712
+ out[key] = String(rawValue);
713
+ }
714
+ return out;
715
+ }
716
+
656
717
  // ---------------------------------------------------------------------------
657
718
  // Main handler factory
658
719
  // ---------------------------------------------------------------------------
@@ -880,23 +941,7 @@ function createLocalApiHandler({ queuePath }) {
880
941
  const insforgeBase = runtime.baseUrl || DEFAULT_BASE_URL;
881
942
  try {
882
943
  const targetUrl = `${insforgeBase.replace(/\/$/, "")}${p}${url.search || ""}`;
883
- const proxyHeaders = {};
884
- for (const [key, value] of Object.entries(req.headers)) {
885
- // Skip headers undici/fetch manages internally. Forwarding the
886
- // inbound content-length (which describes the request body received
887
- // here, not what we're about to send) causes undici to throw
888
- // UND_ERR_INVALID_ARG "invalid content-length header" on every POST,
889
- // turning every /api/auth/* mutation into a 502 — bug present since
890
- // this relay was introduced and surfaced after Node updates made
891
- // undici stricter about forbidden headers.
892
- if (
893
- key === "host" ||
894
- key === "connection" ||
895
- key === "content-length" ||
896
- key === "transfer-encoding"
897
- ) continue;
898
- proxyHeaders[key] = value;
899
- }
944
+ const proxyHeaders = buildProxyHeaders(req.headers);
900
945
  const hasClientCookie = normalizeCookieHeader(proxyHeaders["cookie"]).trim().length > 0;
901
946
  const hasCsrfHeader = typeof proxyHeaders["x-csrf-token"] === "string" && proxyHeaders["x-csrf-token"].trim().length > 0;
902
947
  const relayCsrfToken = getRelayCookieValue(csrfRelayCookieName);
@@ -1013,9 +1058,11 @@ function createLocalApiHandler({ queuePath }) {
1013
1058
  const targetUrl = `${IP_CHECK_TARGET}${targetPath}${url.search || ""}`;
1014
1059
  try {
1015
1060
  // Whitelist forwarded headers — no cookies, no auth, no fingerprintable
1016
- // identity. Only what the upstream needs to negotiate content.
1061
+ // identity. Only what the upstream needs to negotiate content. Do not
1062
+ // set `host` explicitly: undici derives it from the URL, and some
1063
+ // versions reject a manual host header on fetch() (same forbidden-
1064
+ // header family that broke /api/auth/* in 5/13).
1017
1065
  const proxyHeaders = {
1018
- host: "ip.net.coffee",
1019
1066
  accept: req.headers["accept"] || "*/*",
1020
1067
  "accept-language": req.headers["accept-language"] || "en",
1021
1068
  "accept-encoding": req.headers["accept-encoding"] || "gzip",