tokentracker-cli 0.22.3 → 0.23.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.
Files changed (40) hide show
  1. package/README.md +4 -3
  2. package/README.zh-CN.md +4 -3
  3. package/dashboard/dist/assets/{Card-Ce7Bq6YO.js → Card-BgXt23Ak.js} +1 -1
  4. package/dashboard/dist/assets/{DashboardPage-K_pYvsl8.js → DashboardPage-NVAsY99I.js} +1 -1
  5. package/dashboard/dist/assets/{DevicePage-B0t441Pp.js → DevicePage-CWTFzGYj.js} +1 -1
  6. package/dashboard/dist/assets/{FadeIn-rL5OMeU2.js → FadeIn-r11-zwBU.js} +1 -1
  7. package/dashboard/dist/assets/{HeaderGithubStar-CGS4pFqm.js → HeaderGithubStar-ZDYMF__6.js} +1 -1
  8. package/dashboard/dist/assets/{IpCheckPage-Dg8ZSzBs.js → IpCheckPage-cCSBXnv2.js} +1 -1
  9. package/dashboard/dist/assets/{LandingPage-BRTdgQhg.js → LandingPage-DDUROVcN.js} +1 -1
  10. package/dashboard/dist/assets/{LeaderboardPage-DMQ92NBo.js → LeaderboardPage-BaETRtBF.js} +1 -1
  11. package/dashboard/dist/assets/{LeaderboardProfilePage-BYY0tPSp.js → LeaderboardProfilePage-BthYFghU.js} +1 -1
  12. package/dashboard/dist/assets/{LimitsPage-IguYTVRL.js → LimitsPage-BQpint0k.js} +1 -1
  13. package/dashboard/dist/assets/{LoginPage-Ct_y3yvb.js → LoginPage-EUjSrjOv.js} +1 -1
  14. package/dashboard/dist/assets/{PopoverPopup-BKxuCZoU.js → PopoverPopup-DyjmUoRY.js} +1 -1
  15. package/dashboard/dist/assets/{ProviderIcon-BW8DsxOn.js → ProviderIcon-D7GhGvvD.js} +1 -1
  16. package/dashboard/dist/assets/{SettingsPage-BxDpsq6h.js → SettingsPage-9lfQ1c28.js} +1 -1
  17. package/dashboard/dist/assets/{SkillsPage-DGrfA2Ts.js → SkillsPage-Bt7d1kd0.js} +1 -1
  18. package/dashboard/dist/assets/{WidgetsPage-DYLXbN7i.js → WidgetsPage-QfPgS0dO.js} +1 -1
  19. package/dashboard/dist/assets/{WrappedPage-C-NcEsy1.js → WrappedPage-0iiBrhBo.js} +1 -1
  20. package/dashboard/dist/assets/check-CtXuxG1H.js +1 -0
  21. package/dashboard/dist/assets/{chevron-down-sxrI4ACd.js → chevron-down-Br4ZSPLP.js} +1 -1
  22. package/dashboard/dist/assets/{download-tZXJTa2X.js → download-CnQNKpma.js} +1 -1
  23. package/dashboard/dist/assets/{leaderboard-columns-DjTC-eio.js → leaderboard-columns-Bdgb7vS7.js} +1 -1
  24. package/dashboard/dist/assets/main-0bSXJNLn.css +1 -0
  25. package/dashboard/dist/assets/{main-DsrMFwQm.js → main-D7f_DoMp.js} +2 -2
  26. package/dashboard/dist/assets/{use-limits-display-prefs-CzOLYgu5.js → use-limits-display-prefs-B5TQdyrf.js} +1 -1
  27. package/dashboard/dist/assets/{use-native-settings-D6YjGLZJ.js → use-native-settings-CSxKG_Na.js} +1 -1
  28. package/dashboard/dist/assets/{use-reduced-motion-0uADS5dP.js → use-reduced-motion-C9rLBEnX.js} +1 -1
  29. package/dashboard/dist/assets/{use-usage-limits-xh-A-nDC.js → use-usage-limits-Bmok4oKO.js} +1 -1
  30. package/dashboard/dist/index.html +2 -2
  31. package/dashboard/dist/share.html +2 -2
  32. package/package.json +1 -1
  33. package/src/lib/antigravity-paths.js +48 -0
  34. package/src/lib/cursor-config.js +20 -47
  35. package/src/lib/pricing/seed-snapshot.json +1 -1
  36. package/src/lib/rollout.js +81 -157
  37. package/src/lib/skills-manager.js +76 -48
  38. package/src/lib/sqlite-reader.js +136 -0
  39. package/dashboard/dist/assets/check-fi-rlObU.js +0 -1
  40. package/dashboard/dist/assets/main-D55hG1yw.css +0 -1
@@ -1 +1 @@
1
- import{r as a}from"./main-DsrMFwQm.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-D7f_DoMp.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,a5 as x,r as n,aC as g,aD as m,aE as b,aF as u,aG as f,aA as h}from"./main-DsrMFwQm.js";import{C as y}from"./Card-Ce7Bq6YO.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,a5 as x,r as n,aC as g,aD as m,aE as b,aF as u,aG as f,aA as h}from"./main-D7f_DoMp.js";import{C as y}from"./Card-BgXt23Ak.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{aH as t,aI as o,r,aJ as s}from"./main-DsrMFwQm.js";function u(){!t.current&&o();const[e]=r.useState(s.current);return e}export{u};
1
+ import{aH as t,aI as o,r,aJ as s}from"./main-D7f_DoMp.js";function u(){!t.current&&o();const[e]=r.useState(s.current);return e}export{u};
@@ -1 +1 @@
1
- import{r,aj as l}from"./main-DsrMFwQm.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,aj as l}from"./main-D7f_DoMp.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};
@@ -210,8 +210,8 @@
210
210
  ]
211
211
  }
212
212
  </script>
213
- <script type="module" crossorigin src="/assets/main-DsrMFwQm.js"></script>
214
- <link rel="stylesheet" crossorigin href="/assets/main-D55hG1yw.css">
213
+ <script type="module" crossorigin src="/assets/main-D7f_DoMp.js"></script>
214
+ <link rel="stylesheet" crossorigin href="/assets/main-0bSXJNLn.css">
215
215
  </head>
216
216
  <body>
217
217
  <main class="aeo-seed-content" aria-label="Token Tracker AI-readable summary">
@@ -51,8 +51,8 @@
51
51
  "description": "Shareable Token Tracker dashboard snapshot."
52
52
  }
53
53
  </script>
54
- <script type="module" crossorigin src="/assets/main-DsrMFwQm.js"></script>
55
- <link rel="stylesheet" crossorigin href="/assets/main-D55hG1yw.css">
54
+ <script type="module" crossorigin src="/assets/main-D7f_DoMp.js"></script>
55
+ <link rel="stylesheet" crossorigin href="/assets/main-0bSXJNLn.css">
56
56
  </head>
57
57
  <body>
58
58
  <main class="aeo-seed-content" aria-label="Token Tracker share page summary">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokentracker-cli",
3
- "version": "0.22.3",
3
+ "version": "0.23.0",
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": {
@@ -0,0 +1,48 @@
1
+ const fs = require("node:fs");
2
+ const os = require("node:os");
3
+ const path = require("node:path");
4
+
5
+ const MAIN_APP_SUBDIR = "antigravity";
6
+ const IDE_SUBDIR = "antigravity-ide";
7
+
8
+ function geminiHome(env) {
9
+ return path.join(env.HOME || env.USERPROFILE || os.homedir(), ".gemini");
10
+ }
11
+
12
+ function trimmed(value) {
13
+ return typeof value === "string" ? value.trim() : "";
14
+ }
15
+
16
+ function existsDir(dirPath) {
17
+ try {
18
+ return fs.statSync(dirPath).isDirectory();
19
+ } catch (_e) {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ function resolveAntigravitySkillDirs(env = process.env) {
25
+ const override = trimmed(env.TOKENTRACKER_ANTIGRAVITY_HOME);
26
+ if (override) return [path.join(override, "skills")];
27
+
28
+ const home = geminiHome(env);
29
+ const mainSkills = path.join(home, MAIN_APP_SUBDIR, "skills");
30
+ const ideSkills = path.join(home, IDE_SUBDIR, "skills");
31
+ const mainParent = path.join(home, MAIN_APP_SUBDIR);
32
+ const ideParent = path.join(home, IDE_SUBDIR);
33
+
34
+ const dirs = [];
35
+ if (existsDir(mainParent)) dirs.push(mainSkills);
36
+ if (existsDir(ideParent)) dirs.push(ideSkills);
37
+
38
+ // Neither Antigravity install present: fall back to the main-app path so
39
+ // targetList still surfaces a stable path string and the user can install
40
+ // skills now and run Antigravity later. The empty parent will be created on
41
+ // demand by ensureDir; cleanup happens naturally via uninstallSkill.
42
+ if (dirs.length > 0) return dirs;
43
+ return [mainSkills];
44
+ }
45
+
46
+ module.exports = {
47
+ resolveAntigravitySkillDirs,
48
+ };
@@ -1,33 +1,34 @@
1
1
  const os = require("node:os");
2
2
  const path = require("node:path");
3
3
  const fs = require("node:fs");
4
- const cp = require("node:child_process");
5
4
  const https = require("node:https");
6
5
 
7
6
  const { readJson } = require("./fs");
7
+ const { readSqliteFirstValue } = require("./sqlite-reader");
8
8
 
9
9
  // ── Path resolution ──
10
10
 
11
11
  function resolveCursorPaths({ home, platform = process.platform, env = process.env } = {}) {
12
12
  const h = home || os.homedir();
13
+ const pathForPlatform = platform === "win32" ? path.win32 : path.posix;
13
14
  let appDir;
14
15
  if (platform === "darwin") {
15
- appDir = path.join(h, "Library", "Application Support", "Cursor");
16
+ appDir = pathForPlatform.join(h, "Library", "Application Support", "Cursor");
16
17
  } else if (platform === "win32") {
17
18
  const appData =
18
19
  (typeof env.APPDATA === "string" && env.APPDATA.trim()) ||
19
- path.join(h, "AppData", "Roaming");
20
- appDir = path.join(appData, "Cursor");
20
+ pathForPlatform.join(h, "AppData", "Roaming");
21
+ appDir = pathForPlatform.join(appData, "Cursor");
21
22
  } else {
22
23
  const xdg =
23
24
  (typeof env.XDG_CONFIG_HOME === "string" && env.XDG_CONFIG_HOME.trim()) ||
24
- path.join(h, ".config");
25
- appDir = path.join(xdg, "Cursor");
25
+ pathForPlatform.join(h, ".config");
26
+ appDir = pathForPlatform.join(xdg, "Cursor");
26
27
  }
27
28
  return {
28
29
  appDir,
29
- stateDbPath: path.join(appDir, "User", "globalStorage", "state.vscdb"),
30
- cliConfigPath: path.join(h, ".cursor", "cli-config.json"),
30
+ stateDbPath: pathForPlatform.join(appDir, "User", "globalStorage", "state.vscdb"),
31
+ cliConfigPath: pathForPlatform.join(h, ".cursor", "cli-config.json"),
31
32
  };
32
33
  }
33
34
 
@@ -53,43 +54,15 @@ function cursorDebugLog(message, env = process.env) {
53
54
  }
54
55
 
55
56
  function readCursorAccessTokenFromStateDb(stateDbPath, deps = {}) {
56
- const execFileSync = deps.execFileSync || cp.execFileSync;
57
- const requireFn = deps.requireFn || require;
58
- const env = deps.env || process.env;
59
-
60
- try {
61
- return execFileSync("sqlite3", [stateDbPath, CURSOR_ACCESS_TOKEN_SQL], {
62
- encoding: "utf8",
63
- timeout: 5000,
64
- stdio: ["pipe", "pipe", "pipe"],
65
- }).trim();
66
- } catch (sqliteCliErr) {
67
- cursorDebugLog(
68
- `sqlite3 CLI token read failed for ${stateDbPath}: ${sqliteCliErr?.message || sqliteCliErr}`,
69
- env,
70
- );
71
- }
72
-
73
- try {
74
- const { DatabaseSync } = requireFn("node:sqlite");
75
- if (typeof DatabaseSync !== "function") {
76
- cursorDebugLog("node:sqlite DatabaseSync is unavailable", env);
77
- return null;
78
- }
79
- const db = new DatabaseSync(stateDbPath, { readOnly: true });
80
- try {
81
- const row = db.prepare(CURSOR_ACCESS_TOKEN_SQL).get();
82
- return typeof row?.value === "string" ? row.value.trim() : null;
83
- } finally {
84
- db.close();
85
- }
86
- } catch (nodeSqliteErr) {
87
- cursorDebugLog(
88
- `node:sqlite token read failed for ${stateDbPath}: ${nodeSqliteErr?.message || nodeSqliteErr}`,
89
- env,
90
- );
91
- return null;
92
- }
57
+ return readSqliteFirstValue(stateDbPath, CURSOR_ACCESS_TOKEN_SQL, "value", {
58
+ execFileSync: deps.execFileSync,
59
+ requireFn: deps.requireFn,
60
+ env: deps.env,
61
+ stderr: deps.stderr,
62
+ label: "Cursor",
63
+ timeout: 5000,
64
+ maxBuffer: 1024 * 1024,
65
+ });
93
66
  }
94
67
 
95
68
  /**
@@ -103,8 +76,8 @@ function readCursorAccessTokenFromStateDb(stateDbPath, deps = {}) {
103
76
  * - Google sign-in via WorkOS: "google-oauth2|<numeric>" → kept verbatim
104
77
  * - other WorkOS subjects: "github|…", "oidc|…" → kept verbatim
105
78
  */
106
- function extractCursorSessionToken({ home, deps } = {}) {
107
- const { stateDbPath, cliConfigPath } = resolveCursorPaths({ home });
79
+ function extractCursorSessionToken({ home, platform, env, deps } = {}) {
80
+ const { stateDbPath, cliConfigPath } = resolveCursorPaths({ home, platform, env });
108
81
 
109
82
  // 1. Extract JWT from SQLite
110
83
  if (!fs.existsSync(stateDbPath)) {