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.
- package/README.ja.md +459 -0
- package/README.ko.md +459 -0
- package/README.md +47 -6
- package/README.zh-CN.md +47 -6
- package/dashboard/dist/assets/{Card-Cv4wn6W8.js → Card-DdDoM-Gd.js} +1 -1
- package/dashboard/dist/assets/DashboardPage-DqsLRhn6.js +64 -0
- package/dashboard/dist/assets/DevicePage-Dz2WacAK.js +1 -0
- package/dashboard/dist/assets/{FadeIn-DjQyRfLZ.js → FadeIn-CoY8tCtI.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-D2BjLT1b.js → HeaderGithubStar-hr5WjsdY.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-D0uvbHPe.js → IpCheckPage-Dkp3KLz5.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-DGJcVAg7.js → LandingPage-BrXJJj3l.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-Dnt_YLsP.js → LeaderboardPage-DvrAcWn2.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-DM7S9_kG.js → LeaderboardProfilePage-KRQhdatN.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-COomwRa6.js → LimitsPage-Ds-pKbQw.js} +2 -2
- package/dashboard/dist/assets/{LoginPage-k0k50kws.js → LoginPage-t-VKaIRw.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-DctOj5-q.js → PopoverPopup-Cyf_sjKX.js} +2 -2
- package/dashboard/dist/assets/{ProviderIcon-DGlYzr9I.js → ProviderIcon-Dqb9ThUH.js} +1 -1
- package/dashboard/dist/assets/SettingsPage-DhiG9bbD.js +1 -0
- package/dashboard/dist/assets/SkillsPage-BdtuTu9f.js +1 -0
- package/dashboard/dist/assets/{WidgetsPage-DsMj8Qcz.js → WidgetsPage--n-BUbXK.js} +1 -1
- package/dashboard/dist/assets/WrappedPage-DRoff4y0.js +1 -0
- package/dashboard/dist/assets/check-BuIsQw1B.js +1 -0
- package/dashboard/dist/assets/{chevron-down-kcaroSaH.js → chevron-down-DvuPNrFG.js} +1 -1
- package/dashboard/dist/assets/{download-DKMK6oF8.js → download-LwtMKzsl.js} +1 -1
- package/dashboard/dist/assets/{leaderboard-columns-BZ06dD2h.js → leaderboard-columns-C01mJBwn.js} +1 -1
- package/dashboard/dist/assets/main-A_x5MMU-.css +1 -0
- package/dashboard/dist/assets/{main-DKVBnAOd.js → main-Csid8osN.js} +62 -17
- package/dashboard/dist/assets/{use-limits-display-prefs-Bx-K-27B.js → use-limits-display-prefs-BP17xuqs.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-DtuifRKC.js → use-native-settings-C-RMuUxI.js} +1 -1
- package/dashboard/dist/assets/{use-reduced-motion-Cen-UCKO.js → use-reduced-motion-CPJKbom2.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-CAWz6ijv.js → use-usage-limits-DIE-GP0H.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +3 -2
- package/src/cli.js +11 -0
- package/src/commands/device-login.js +161 -0
- package/src/commands/status.js +199 -1
- package/src/commands/sync.js +85 -2
- package/src/commands/wrapped.js +150 -0
- package/src/lib/local-api.js +37 -2
- package/src/lib/passive-mode.js +185 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +913 -0
- package/src/lib/wrapped-aggregator.js +225 -0
- package/dashboard/dist/assets/DashboardPage-DsfcNgai.js +0 -1
- package/dashboard/dist/assets/SettingsPage-D2sqM9g_.js +0 -1
- package/dashboard/dist/assets/SkillsPage-B8K--edc.js +0 -1
- package/dashboard/dist/assets/main-DX38hz5f.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as a}from"./main-
|
|
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};
|
package/dashboard/dist/assets/{use-native-settings-DtuifRKC.js → use-native-settings-C-RMuUxI.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
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};
|
package/dashboard/dist/assets/{use-reduced-motion-Cen-UCKO.js → use-reduced-motion-CPJKbom2.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
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-
|
|
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-
|
|
214
|
-
<link rel="stylesheet" crossorigin href="/assets/main-
|
|
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-
|
|
55
|
-
<link rel="stylesheet" crossorigin href="/assets/main-
|
|
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.
|
|
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 };
|
package/src/commands/status.js
CHANGED
|
@@ -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"
|
|
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);
|
package/src/commands/sync.js
CHANGED
|
@@ -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:",
|