tokentracker-cli 0.21.3 → 0.22.1

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 (48) hide show
  1. package/README.ja.md +459 -0
  2. package/README.ko.md +459 -0
  3. package/README.md +47 -6
  4. package/README.zh-CN.md +47 -6
  5. package/dashboard/dist/assets/{Card-Cv4wn6W8.js → Card-DdDoM-Gd.js} +1 -1
  6. package/dashboard/dist/assets/DashboardPage-DqsLRhn6.js +64 -0
  7. package/dashboard/dist/assets/DevicePage-Dz2WacAK.js +1 -0
  8. package/dashboard/dist/assets/{FadeIn-DjQyRfLZ.js → FadeIn-CoY8tCtI.js} +1 -1
  9. package/dashboard/dist/assets/{HeaderGithubStar-D2BjLT1b.js → HeaderGithubStar-hr5WjsdY.js} +1 -1
  10. package/dashboard/dist/assets/{IpCheckPage-D0uvbHPe.js → IpCheckPage-Dkp3KLz5.js} +1 -1
  11. package/dashboard/dist/assets/{LandingPage-DGJcVAg7.js → LandingPage-BrXJJj3l.js} +1 -1
  12. package/dashboard/dist/assets/{LeaderboardPage-Dnt_YLsP.js → LeaderboardPage-DvrAcWn2.js} +1 -1
  13. package/dashboard/dist/assets/{LeaderboardProfilePage-DM7S9_kG.js → LeaderboardProfilePage-KRQhdatN.js} +1 -1
  14. package/dashboard/dist/assets/{LimitsPage-COomwRa6.js → LimitsPage-Ds-pKbQw.js} +2 -2
  15. package/dashboard/dist/assets/{LoginPage-k0k50kws.js → LoginPage-t-VKaIRw.js} +1 -1
  16. package/dashboard/dist/assets/{PopoverPopup-DctOj5-q.js → PopoverPopup-Cyf_sjKX.js} +2 -2
  17. package/dashboard/dist/assets/{ProviderIcon-DGlYzr9I.js → ProviderIcon-Dqb9ThUH.js} +1 -1
  18. package/dashboard/dist/assets/SettingsPage-DhiG9bbD.js +1 -0
  19. package/dashboard/dist/assets/SkillsPage-BdtuTu9f.js +1 -0
  20. package/dashboard/dist/assets/{WidgetsPage-DsMj8Qcz.js → WidgetsPage--n-BUbXK.js} +1 -1
  21. package/dashboard/dist/assets/WrappedPage-DRoff4y0.js +1 -0
  22. package/dashboard/dist/assets/check-BuIsQw1B.js +1 -0
  23. package/dashboard/dist/assets/{chevron-down-kcaroSaH.js → chevron-down-DvuPNrFG.js} +1 -1
  24. package/dashboard/dist/assets/{download-DKMK6oF8.js → download-LwtMKzsl.js} +1 -1
  25. package/dashboard/dist/assets/{leaderboard-columns-BZ06dD2h.js → leaderboard-columns-C01mJBwn.js} +1 -1
  26. package/dashboard/dist/assets/main-A_x5MMU-.css +1 -0
  27. package/dashboard/dist/assets/{main-DKVBnAOd.js → main-Csid8osN.js} +62 -17
  28. package/dashboard/dist/assets/{use-limits-display-prefs-Bx-K-27B.js → use-limits-display-prefs-BP17xuqs.js} +1 -1
  29. package/dashboard/dist/assets/{use-native-settings-DtuifRKC.js → use-native-settings-C-RMuUxI.js} +1 -1
  30. package/dashboard/dist/assets/{use-reduced-motion-Cen-UCKO.js → use-reduced-motion-CPJKbom2.js} +1 -1
  31. package/dashboard/dist/assets/{use-usage-limits-CAWz6ijv.js → use-usage-limits-DIE-GP0H.js} +1 -1
  32. package/dashboard/dist/index.html +2 -2
  33. package/dashboard/dist/share.html +2 -2
  34. package/package.json +3 -2
  35. package/src/cli.js +11 -0
  36. package/src/commands/device-login.js +161 -0
  37. package/src/commands/status.js +199 -1
  38. package/src/commands/sync.js +85 -2
  39. package/src/commands/wrapped.js +150 -0
  40. package/src/lib/local-api.js +37 -2
  41. package/src/lib/passive-mode.js +185 -0
  42. package/src/lib/pricing/seed-snapshot.json +1 -1
  43. package/src/lib/rollout.js +913 -0
  44. package/src/lib/wrapped-aggregator.js +225 -0
  45. package/dashboard/dist/assets/DashboardPage-DsfcNgai.js +0 -1
  46. package/dashboard/dist/assets/SettingsPage-D2sqM9g_.js +0 -1
  47. package/dashboard/dist/assets/SkillsPage-B8K--edc.js +0 -1
  48. package/dashboard/dist/assets/main-DX38hz5f.css +0 -1
@@ -1 +1 @@
1
- import{r as a}from"./main-DKVBnAOd.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-Csid8osN.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{D as a,a5 as x,r as n,aB as g,aC as m,aD as b,aE as u,aF as f,az as h}from"./main-DKVBnAOd.js";import{C as y}from"./Card-Cv4wn6W8.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-Csid8osN.js";import{C as y}from"./Card-DdDoM-Gd.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{aG as t,aH as o,r,aI as s}from"./main-DKVBnAOd.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-Csid8osN.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-DKVBnAOd.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-Csid8osN.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-DKVBnAOd.js"></script>
214
- <link rel="stylesheet" crossorigin href="/assets/main-DX38hz5f.css">
213
+ <script type="module" crossorigin src="/assets/main-Csid8osN.js"></script>
214
+ <link rel="stylesheet" crossorigin href="/assets/main-A_x5MMU-.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-DKVBnAOd.js"></script>
55
- <link rel="stylesheet" crossorigin href="/assets/main-DX38hz5f.css">
54
+ <script type="module" crossorigin src="/assets/main-Csid8osN.js"></script>
55
+ <link rel="stylesheet" crossorigin href="/assets/main-A_x5MMU-.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,7 +1,7 @@
1
1
  {
2
2
  "name": "tokentracker-cli",
3
- "version": "0.21.3",
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)",
3
+ "version": "0.22.1",
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": {
7
7
  "tokentracker-cli": "bin/tracker.js",
@@ -41,6 +41,7 @@
41
41
  "validate:ui-hardcode": "node scripts/ops/validate-ui-hardcode.cjs"
42
42
  },
43
43
  "dependencies": {
44
+ "@mongodb-js/zstd": "^2.0.1",
44
45
  "undici": "^8.2.0"
45
46
  },
46
47
  "devDependencies": {
package/src/cli.js CHANGED
@@ -5,6 +5,8 @@ const { cmdDiagnostics } = require("./commands/diagnostics");
5
5
  const { cmdDoctor } = require("./commands/doctor");
6
6
  const { cmdUninstall } = require("./commands/uninstall");
7
7
  const { cmdServe } = require("./commands/serve");
8
+ const { cmdDeviceLogin } = require("./commands/device-login");
9
+ const { cmdWrapped } = require("./commands/wrapped");
8
10
 
9
11
  async function run(argv) {
10
12
  const [command, ...rest] = argv;
@@ -42,6 +44,12 @@ async function run(argv) {
42
44
  case "uninstall":
43
45
  await cmdUninstall(rest);
44
46
  return;
47
+ case "device-login":
48
+ await cmdDeviceLogin(rest);
49
+ return;
50
+ case "wrapped":
51
+ await cmdWrapped(rest);
52
+ return;
45
53
  default:
46
54
  throw new Error(`Unknown command: ${command}`);
47
55
  }
@@ -62,6 +70,8 @@ function printHelp() {
62
70
  " npx tokentracker [--debug] diagnostics [--out diagnostics.json]",
63
71
  " npx tokentracker [--debug] doctor [--json] [--out doctor.json] [--base-url <url>]",
64
72
  " npx tokentracker [--debug] uninstall [--purge]",
73
+ " npx tokentracker [--debug] device-login [--json] [--base-url <url>]",
74
+ " npx tokentracker [--debug] wrapped [--year 2026] [--json]",
65
75
  "",
66
76
  "Notes:",
67
77
  " - init: consent first, local setup next, browser sign-in last.",
@@ -75,6 +85,7 @@ function printHelp() {
75
85
  " - sync parses ~/.codex/sessions/**/rollout-*.jsonl and ~/.code/sessions/**/rollout-*.jsonl, then uploads token deltas.",
76
86
  " - --from-openclaw marks sync runs triggered by OpenClaw hooks.",
77
87
  " - --debug shows original backend errors.",
88
+ " - device-login pairs a headless CLI / SSH session with a browser sign-in (15-min code).",
78
89
  "",
79
90
  ].join("\n"),
80
91
  );
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+
3
+ const os = require("node:os");
4
+ const path = require("node:path");
5
+ const fs = require("node:fs/promises");
6
+
7
+ const { readJson, writeJson } = require("../lib/fs");
8
+ const { resolveTrackerPaths } = require("../lib/tracker-paths");
9
+
10
+ const DEFAULT_BASE_URL = "https://srctyff5.us-east.insforge.app";
11
+ const POLL_INTERVAL_MS = 5_000;
12
+ const ABSOLUTE_TIMEOUT_MS = 16 * 60 * 1000; // matches the 15-min server window with a small buffer
13
+
14
+ function readBaseUrl(config) {
15
+ return (
16
+ process.env.TOKENTRACKER_BASE_URL ||
17
+ process.env.TOKENTRACKER_API_URL ||
18
+ config?.baseUrl ||
19
+ DEFAULT_BASE_URL
20
+ );
21
+ }
22
+
23
+ async function authorize({ baseUrl, clientInfo }) {
24
+ const res = await fetch(`${baseUrl}/functions/tokentracker-device-flow-authorize`, {
25
+ method: "POST",
26
+ headers: { "Content-Type": "application/json" },
27
+ body: JSON.stringify({ client_info: clientInfo }),
28
+ });
29
+ if (!res.ok) {
30
+ const text = await res.text();
31
+ throw new Error(`authorize failed (HTTP ${res.status}): ${text.slice(0, 200)}`);
32
+ }
33
+ return res.json();
34
+ }
35
+
36
+ async function pollOnce({ baseUrl, deviceCode }) {
37
+ const res = await fetch(`${baseUrl}/functions/tokentracker-device-flow-poll`, {
38
+ method: "POST",
39
+ headers: { "Content-Type": "application/json" },
40
+ body: JSON.stringify({ device_code: deviceCode }),
41
+ });
42
+ const data = await res.json().catch(() => ({}));
43
+ // 404 = unknown, 410 = expired, 200 = {status, user_id?, device_token?}.
44
+ // Anything else (502 from a misconfigured edge, 5xx during deploy, …) must
45
+ // bubble up as a network-style error — masking it as "unknown" would tell
46
+ // the user their device_code was evicted when it wasn't.
47
+ if (!res.ok && res.status !== 404 && res.status !== 410) {
48
+ throw new Error(`poll HTTP ${res.status}: ${(data?.error ?? "").toString().slice(0, 200)}`);
49
+ }
50
+ return {
51
+ status: data.status ?? "unknown",
52
+ user_id: data.user_id ?? null,
53
+ deviceToken: data.device_token ?? data.deviceToken ?? null,
54
+ deviceId: data.device_id ?? data.deviceId ?? null,
55
+ httpStatus: res.status,
56
+ };
57
+ }
58
+
59
+ function sleep(ms) {
60
+ return new Promise((resolve) => setTimeout(resolve, ms));
61
+ }
62
+
63
+ async function cmdDeviceLogin(argv = [], options = {}) {
64
+ const opts = parseArgs(argv);
65
+ const home = options.home || os.homedir();
66
+ const sleepFn = options.sleep || sleep;
67
+ const { trackerDir } = await resolveTrackerPaths({ home });
68
+ const configPath = path.join(trackerDir, "config.json");
69
+ const config = (await readJson(configPath)) || {};
70
+ const baseUrl = opts.baseUrl || readBaseUrl(config);
71
+
72
+ const clientInfo = `${os.platform()}-${os.arch()} ${os.hostname()}`;
73
+ process.stdout.write(`Requesting device code from ${baseUrl}...\n`);
74
+ const authResp = await authorize({ baseUrl, clientInfo });
75
+
76
+ if (opts.json) {
77
+ process.stdout.write(JSON.stringify(authResp, null, 2) + "\n");
78
+ } else {
79
+ process.stdout.write(
80
+ [
81
+ "",
82
+ " Sign in from a browser:",
83
+ ` ${authResp.verification_uri_complete || authResp.verification_uri}`,
84
+ "",
85
+ ` Or visit ${authResp.verification_uri} and enter the code:`,
86
+ "",
87
+ ` ${authResp.user_code}`,
88
+ "",
89
+ ` This code expires in ${Math.round(authResp.expires_in / 60)} minutes.`,
90
+ " Polling every 5 seconds until you approve…",
91
+ "",
92
+ ].join("\n"),
93
+ );
94
+ }
95
+
96
+ const startedAt = Date.now();
97
+ let consecutiveErrors = 0;
98
+ const MAX_BACKOFF_MS = 30_000;
99
+ while (Date.now() - startedAt < ABSOLUTE_TIMEOUT_MS) {
100
+ // Exponential backoff on consecutive network failures (capped at 30s) so
101
+ // a flaky network doesn't hammer the server at the full 5s cadence for
102
+ // the entire 15-minute window. ±20% jitter on retries prevents
103
+ // thundering-herd reconnects when many CLIs lose connectivity at once.
104
+ let wait =
105
+ consecutiveErrors === 0
106
+ ? POLL_INTERVAL_MS
107
+ : Math.min(POLL_INTERVAL_MS * Math.pow(2, consecutiveErrors - 1), MAX_BACKOFF_MS);
108
+ if (consecutiveErrors > 0) {
109
+ const jitter = wait * 0.2 * (Math.random() * 2 - 1);
110
+ wait = Math.max(POLL_INTERVAL_MS, wait + jitter);
111
+ }
112
+ await sleepFn(wait);
113
+ let result;
114
+ try {
115
+ result = await pollOnce({ baseUrl, deviceCode: authResp.device_code });
116
+ consecutiveErrors = 0;
117
+ } catch (e) {
118
+ consecutiveErrors++;
119
+ process.stderr.write(`poll error (retry ${consecutiveErrors}): ${e?.message || e}\n`);
120
+ continue;
121
+ }
122
+ if (result.status === "approved" && result.user_id) {
123
+ if (!result.deviceToken) {
124
+ throw new Error("device login approved but server did not return a device token");
125
+ }
126
+ const next = {
127
+ ...config,
128
+ baseUrl,
129
+ user_id: result.user_id,
130
+ deviceToken: result.deviceToken,
131
+ deviceId: result.deviceId || config.deviceId,
132
+ device_login_at: new Date().toISOString(),
133
+ };
134
+ await writeJson(configPath, next);
135
+ process.stdout.write(`\n✓ Approved. device token written to ${configPath}\n`);
136
+ return;
137
+ }
138
+ if (result.status === "expired") {
139
+ throw new Error("device_code expired — re-run `tracker device-login`");
140
+ }
141
+ if (result.status === "unknown") {
142
+ throw new Error("device_code is unknown — server may have evicted it");
143
+ }
144
+ // status === "pending" → just keep polling silently
145
+ }
146
+ throw new Error("device-login timed out without approval");
147
+ }
148
+
149
+ function parseArgs(argv) {
150
+ const out = { json: false, baseUrl: null };
151
+ for (let i = 0; i < argv.length; i++) {
152
+ const a = argv[i];
153
+ if (a === "--json") out.json = true;
154
+ else if (a === "--base-url") {
155
+ out.baseUrl = argv[++i] || null;
156
+ } else throw new Error(`Unknown option: ${a}`);
157
+ }
158
+ return out;
159
+ }
160
+
161
+ module.exports = { cmdDeviceLogin, authorize, pollOnce };
@@ -29,6 +29,7 @@ const {
29
29
  normalizeState: normalizeUploadState,
30
30
  } = require("../lib/upload-throttle");
31
31
  const { collectTrackerDiagnostics } = require("../lib/diagnostics");
32
+ const { detectPassiveProviders, isPassiveModeActive } = require("../lib/passive-mode");
32
33
  const { probeOpenclawHookState } = require("../lib/openclaw-hook");
33
34
  const {
34
35
  probeOpenclawSessionPluginState,
@@ -47,6 +48,9 @@ const {
47
48
  resolveCraftSessionFiles,
48
49
  resolveCraftConfigDir,
49
50
  resolveKilocodeTaskFiles,
51
+ resolveRoocodeTaskFiles,
52
+ resolveZedDbPath,
53
+ resolveGooseDbPath,
50
54
  resolveGrokBuildSessions,
51
55
  } = require("../lib/rollout");
52
56
  const { probeGrokHookState, resolveGrokHome } = require("../lib/grok-hook");
@@ -214,6 +218,19 @@ async function cmdStatus(argv = []) {
214
218
  const kilocodeTaskFiles = resolveKilocodeTaskFiles(process.env);
215
219
  const kilocodeInstalled = kilocodeTaskFiles.length > 0;
216
220
 
221
+ // Roo Code VS Code extension — same Cline-derived ui_messages.json format,
222
+ // different globalStorage subdir (rooveterinaryinc.roo-cline).
223
+ const roocodeTaskFiles = resolveRoocodeTaskFiles(process.env);
224
+ const roocodeInstalled = roocodeTaskFiles.length > 0;
225
+
226
+ // Zed Agent — passive read of hosted threads.db (skips external ACP agents).
227
+ const zedDbPath = resolveZedDbPath(process.env);
228
+ const zedInstalled = fssync.existsSync(zedDbPath);
229
+
230
+ // Goose (Block) — passive cumulative-delta read of sessions.db.
231
+ const gooseDbPath = resolveGooseDbPath(process.env);
232
+ const gooseInstalled = fssync.existsSync(gooseDbPath);
233
+
217
234
  // Grok Build (xAI TUI)
218
235
  const grokHookState = await probeGrokHookState({ home, trackerDir, env: process.env });
219
236
  const grokSessions = grokHookState.hasGrokInstall || grokHookState.sessionsDir
@@ -228,6 +245,114 @@ async function cmdStatus(argv = []) {
228
245
  otel: copilotOtel,
229
246
  });
230
247
 
248
+ // Detect passive-mode providers exactly once — both the JSON/light path
249
+ // and the human-readable path consume this, and each call hits 5 readdir
250
+ // syscalls (~5–10ms cold). Memoize.
251
+ const passiveProviders = detectPassiveProviders({
252
+ home,
253
+ hookStatus: {
254
+ codex_notify: notifyConfigured,
255
+ every_code_notify: everyCodeConfigured,
256
+ claude: claudeHookConfigured,
257
+ gemini: geminiHookConfigured,
258
+ opencode_plugin: opencodePluginConfigured,
259
+ openclaw_session_plugin: Boolean(openclawSessionPluginState?.configured),
260
+ codebuddy: Boolean(codebuddyHookConfigured),
261
+ grok: Boolean(grokHookState?.configured),
262
+ },
263
+ });
264
+
265
+ if (opts.json || opts.light) {
266
+ const summary = {
267
+ generated_at: new Date().toISOString(),
268
+ base_url: config?.baseUrl || null,
269
+ device_token_set: Boolean(config?.deviceToken),
270
+ queue: {
271
+ pending_bytes: pendingBytes,
272
+ size_bytes: queueSize,
273
+ offset: queueState.offset || 0,
274
+ },
275
+ last_parse: cursors?.updatedAt || null,
276
+ last_notify: lastNotify || null,
277
+ last_openclaw_sync: lastOpenclawSync || null,
278
+ last_notify_spawn: lastNotifySpawn || null,
279
+ last_upload: lastUpload || null,
280
+ next_upload_after: nextUpload || null,
281
+ backoff_until: backoffUntil || null,
282
+ last_upload_error: lastUploadError || null,
283
+ auto_retry: autoRetry || null,
284
+ hooks: {
285
+ codex_notify: notifyConfigured,
286
+ every_code_notify: everyCodeConfigured,
287
+ claude: claudeHookConfigured,
288
+ gemini: geminiHookConfigured,
289
+ opencode_plugin: opencodePluginConfigured,
290
+ openclaw_session_plugin: Boolean(openclawSessionPluginState?.configured),
291
+ openclaw_legacy: Boolean(openclawHookState?.configured),
292
+ codebuddy: codebuddyInstalled ? Boolean(codebuddyHookConfigured) : null,
293
+ grok: grokInstalled ? Boolean(grokHookState?.configured) : null,
294
+ },
295
+ providers: {
296
+ kimi_code: kimiInstalled
297
+ ? { installed: true, files: kimiWireFiles.length }
298
+ : { installed: false },
299
+ kiro_cli: kiroCliInstalled
300
+ ? { installed: true, detail: kiroCliDbPath }
301
+ : { installed: false },
302
+ codebuddy: codebuddyInstalled
303
+ ? { installed: true, files: codebuddyFiles.length }
304
+ : { installed: false },
305
+ omp: ompInstalled
306
+ ? { installed: true, files: ompFiles.length }
307
+ : { installed: false },
308
+ pi: piInstalled
309
+ ? { installed: true, files: piFiles.length }
310
+ : { installed: false },
311
+ craft: craftInstalled
312
+ ? { installed: true, files: craftFiles.length }
313
+ : { installed: false },
314
+ kilo_cli: kiloInstalled
315
+ ? { installed: true, detail: kiloDbPath }
316
+ : { installed: false },
317
+ kilocode: kilocodeInstalled
318
+ ? { installed: true, files: kilocodeTaskFiles.length }
319
+ : { installed: false },
320
+ roocode: roocodeInstalled
321
+ ? { installed: true, files: roocodeTaskFiles.length }
322
+ : { installed: false },
323
+ zed: zedInstalled ? { installed: true, detail: zedDbPath } : { installed: false },
324
+ goose: gooseInstalled
325
+ ? { installed: true, detail: gooseDbPath }
326
+ : { installed: false },
327
+ grok_build: grokInstalled
328
+ ? {
329
+ installed: true,
330
+ files: grokSessions.length,
331
+ detail: grokHookState.configured ? "hook installed" : "detected",
332
+ }
333
+ : { installed: false },
334
+ },
335
+ copilot: {
336
+ token_set: Boolean(copilotToken),
337
+ otel_has_files: Boolean(copilotOtel.otel_has_files),
338
+ otel_path: copilotOtel.otel_path || null,
339
+ otel_enabled: Boolean(copilotOtel.otel_enabled),
340
+ },
341
+ passive_mode: {
342
+ active: isPassiveModeActive(passiveProviders),
343
+ providers: passiveProviders,
344
+ },
345
+ subscriptions,
346
+ };
347
+
348
+ if (opts.json) {
349
+ process.stdout.write(JSON.stringify(summary, null, 2) + "\n");
350
+ return;
351
+ }
352
+ process.stdout.write(renderLightTable(summary) + "\n");
353
+ return;
354
+ }
355
+
231
356
  process.stdout.write(
232
357
  [
233
358
  "Status:",
@@ -274,6 +399,22 @@ async function cmdStatus(argv = []) {
274
399
  kilocodeInstalled
275
400
  ? `- Kilo Code (VS Code extension): passive reader (${kilocodeTaskFiles.length} task${kilocodeTaskFiles.length !== 1 ? "s" : ""} across ${new Set(kilocodeTaskFiles.map((t) => t.ide)).size} IDE${new Set(kilocodeTaskFiles.map((t) => t.ide)).size !== 1 ? "s" : ""})`
276
401
  : null,
402
+ roocodeInstalled
403
+ ? `- Roo Code (VS Code extension): passive reader (${roocodeTaskFiles.length} task${roocodeTaskFiles.length !== 1 ? "s" : ""} across ${new Set(roocodeTaskFiles.map((t) => t.ide)).size} IDE${new Set(roocodeTaskFiles.map((t) => t.ide)).size !== 1 ? "s" : ""})`
404
+ : null,
405
+ zedInstalled
406
+ ? `- Zed Agent: passive reader (threads.db, hosted models only)`
407
+ : null,
408
+ gooseInstalled
409
+ ? `- Goose (Block): passive reader (sessions.db, cumulative-delta)`
410
+ : null,
411
+ ...(() => {
412
+ const passive = passiveProviders.filter((p) => p.passive);
413
+ if (passive.length === 0) return [];
414
+ return [
415
+ `- Passive mode: ${passive.length} provider${passive.length !== 1 ? "s" : ""} reading logs without hooks (${passive.map((p) => `${p.name}: ${p.hook_failure_reason || "hook unset"}`).join("; ")})`,
416
+ ];
417
+ })(),
277
418
  grokInstalled
278
419
  ? `- Grok Build (xAI): ${grokHookState.configured ? "hook installed" : "detected"} (${grokSessions.length} session${grokSessions.length !== 1 ? "s" : ""} found, hook: ${grokHookState.configured ? "yes" : "no"})`
279
420
  : null,
@@ -348,13 +489,19 @@ function formatSubscriptionLine(entry = {}) {
348
489
  function parseArgs(argv) {
349
490
  const out = {
350
491
  diagnostics: false,
492
+ json: false,
493
+ light: false,
494
+ noSpinner: false,
351
495
  probeKeychain: false,
352
496
  probeKeychainDetails: false,
353
497
  };
354
498
 
355
499
  for (let i = 0; i < argv.length; i++) {
356
500
  const a = argv[i];
357
- if (a === "--diagnostics" || a === "--json") out.diagnostics = true;
501
+ if (a === "--diagnostics") out.diagnostics = true;
502
+ else if (a === "--json") out.json = true;
503
+ else if (a === "--light") out.light = true;
504
+ else if (a === "--no-spinner") out.noSpinner = true;
358
505
  else if (a === "--probe-keychain") out.probeKeychain = true;
359
506
  else if (a === "--probe-keychain-details") {
360
507
  out.probeKeychainDetails = true;
@@ -365,6 +512,57 @@ function parseArgs(argv) {
365
512
  return out;
366
513
  }
367
514
 
515
+ // Pure renderer: turn the structured summary into a fixed-width ASCII table.
516
+ // "light" output is for AI agents and CI: deterministic columns, no emoji or
517
+ // spinner side effects, easy to grep. Returns a string (caller adds trailing
518
+ // newline).
519
+ function renderLightTable(summary) {
520
+ const rows = [];
521
+ const push = (k, v) => rows.push([k, v == null || v === "" ? "—" : String(v)]);
522
+
523
+ push("Base URL", summary.base_url);
524
+ push("Device token", summary.device_token_set ? "set" : "unset");
525
+ push("Queue pending (bytes)", summary.queue.pending_bytes);
526
+ push("Queue size (bytes)", summary.queue.size_bytes);
527
+ push("Last parse", summary.last_parse);
528
+ push("Last notify", summary.last_notify);
529
+ push("Last upload", summary.last_upload);
530
+ push("Next upload after", summary.next_upload_after);
531
+ push("Backoff until", summary.backoff_until);
532
+ if (summary.last_upload_error) push("Last upload error", summary.last_upload_error);
533
+
534
+ for (const [name, state] of Object.entries(summary.hooks || {})) {
535
+ push(`Hook · ${name}`, state ? "set" : "unset");
536
+ }
537
+
538
+ for (const [name, info] of Object.entries(summary.providers || {})) {
539
+ const detail = [];
540
+ if (typeof info.installed === "boolean") detail.push(info.installed ? "installed" : "not installed");
541
+ if (typeof info.files === "number") detail.push(`${info.files} file${info.files !== 1 ? "s" : ""}`);
542
+ if (info.detail) detail.push(info.detail);
543
+ push(`Provider · ${name}`, detail.length ? detail.join(", ") : "—");
544
+ }
545
+
546
+ if (summary.passive_mode) {
547
+ push("Passive mode active", summary.passive_mode.active ? "yes" : "no");
548
+ for (const p of summary.passive_mode.providers || []) {
549
+ if (p.passive) {
550
+ push(`Passive · ${p.name}`, `hook ${p.hook_failure_reason || "missing"}, logs present`);
551
+ }
552
+ }
553
+ }
554
+
555
+ const keyWidth = Math.max(...rows.map(([k]) => k.length), 8);
556
+ const valWidth = Math.max(...rows.map(([, v]) => v.length), 8);
557
+ const sep = `+${"-".repeat(keyWidth + 2)}+${"-".repeat(valWidth + 2)}+`;
558
+ const lines = [sep, `| ${"Key".padEnd(keyWidth)} | ${"Value".padEnd(valWidth)} |`, sep];
559
+ for (const [k, v] of rows) {
560
+ lines.push(`| ${k.padEnd(keyWidth)} | ${v.padEnd(valWidth)} |`);
561
+ }
562
+ lines.push(sep);
563
+ return lines.join("\n");
564
+ }
565
+
368
566
  async function safeStatSize(p) {
369
567
  try {
370
568
  const st = await fs.stat(p);
@@ -46,6 +46,12 @@ const {
46
46
  parseKiroCliIncremental,
47
47
  resolveKilocodeTaskFiles,
48
48
  parseKilocodeIncremental,
49
+ resolveRoocodeTaskFiles,
50
+ parseRoocodeIncremental,
51
+ resolveZedDbPath,
52
+ parseZedIncremental,
53
+ resolveGooseDbPath,
54
+ parseGooseIncremental,
49
55
  bucketKey,
50
56
  totalsKey,
51
57
  claudeMessageDedupKey,
@@ -427,6 +433,77 @@ async function cmdSync(argv) {
427
433
  });
428
434
  }
429
435
 
436
+ // ── Goose (Block) — SQLite sessions with cumulative tokens per session ──
437
+ const gooseDbPath = resolveGooseDbPath(process.env);
438
+ let gooseResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
439
+ if (fssync.existsSync(gooseDbPath)) {
440
+ if (progress?.enabled) {
441
+ progress.start(`Parsing Goose ${renderBar(0)} 0 sessions | buckets 0`);
442
+ }
443
+ gooseResult = await parseGooseIncremental({
444
+ dbPath: gooseDbPath,
445
+ cursors,
446
+ queuePath,
447
+ onProgress: (p) => {
448
+ if (!progress?.enabled) return;
449
+ const pct = p.total > 0 ? p.index / p.total : 1;
450
+ progress.update(
451
+ `Parsing Goose ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(
452
+ p.total,
453
+ )} sessions | buckets ${formatNumber(p.bucketsQueued)}`,
454
+ );
455
+ },
456
+ });
457
+ }
458
+
459
+ // ── Zed Agent (hosted models only; cumulative-delta over SQLite threads) ──
460
+ const zedDbPath = resolveZedDbPath(process.env);
461
+ let zedResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
462
+ if (fssync.existsSync(zedDbPath)) {
463
+ if (progress?.enabled) {
464
+ progress.start(`Parsing Zed Agent ${renderBar(0)} 0 threads | buckets 0`);
465
+ }
466
+ zedResult = await parseZedIncremental({
467
+ dbPath: zedDbPath,
468
+ cursors,
469
+ queuePath,
470
+ onProgress: (p) => {
471
+ if (!progress?.enabled) return;
472
+ const pct = p.total > 0 ? p.index / p.total : 1;
473
+ progress.update(
474
+ `Parsing Zed Agent ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(
475
+ p.total,
476
+ )} threads | buckets ${formatNumber(p.bucketsQueued)}`,
477
+ );
478
+ },
479
+ });
480
+ }
481
+
482
+ // ── Roo Code VS Code extension (Cline-derived; rooveterinaryinc.roo-cline) ──
483
+ const roocodeTaskFiles = resolveRoocodeTaskFiles(process.env);
484
+ let roocodeResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
485
+ if (roocodeTaskFiles.length > 0) {
486
+ if (progress?.enabled) {
487
+ progress.start(
488
+ `Parsing Roo Code ${renderBar(0)} 0/${formatNumber(roocodeTaskFiles.length)} tasks | buckets 0`,
489
+ );
490
+ }
491
+ roocodeResult = await parseRoocodeIncremental({
492
+ taskFiles: roocodeTaskFiles,
493
+ cursors,
494
+ queuePath,
495
+ onProgress: (p) => {
496
+ if (!progress?.enabled) return;
497
+ const pct = p.total > 0 ? p.index / p.total : 1;
498
+ progress.update(
499
+ `Parsing Roo Code ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(
500
+ p.total,
501
+ )} tasks | buckets ${formatNumber(p.bucketsQueued)}`,
502
+ );
503
+ },
504
+ });
505
+ }
506
+
430
507
  // ── Cursor (API-based) ──
431
508
  // One-time migration: earlier CLI versions mis-parsed the Cursor CSV after
432
509
  // Cursor inserted new "Cloud Agent ID"/"Automation ID" columns, writing
@@ -868,7 +945,10 @@ async function cmdSync(argv) {
868
945
  grokResult.recordsProcessed +
869
946
  copilotResult.recordsProcessed +
870
947
  kiloResult.messagesProcessed +
871
- kilocodeResult.recordsProcessed;
948
+ kilocodeResult.recordsProcessed +
949
+ roocodeResult.recordsProcessed +
950
+ zedResult.recordsProcessed +
951
+ gooseResult.recordsProcessed;
872
952
  const totalBuckets =
873
953
  parseResult.bucketsQueued +
874
954
  openclawResult.bucketsQueued +
@@ -888,7 +968,10 @@ async function cmdSync(argv) {
888
968
  grokResult.bucketsQueued +
889
969
  copilotResult.bucketsQueued +
890
970
  kiloResult.bucketsQueued +
891
- kilocodeResult.bucketsQueued;
971
+ kilocodeResult.bucketsQueued +
972
+ roocodeResult.bucketsQueued +
973
+ zedResult.bucketsQueued +
974
+ gooseResult.bucketsQueued;
892
975
  process.stdout.write(
893
976
  [
894
977
  "Sync finished:",