tokentracker-cli 1.3.16 → 1.4.2
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.de.md +5 -4
- package/README.ja.md +5 -4
- package/README.ko.md +5 -4
- package/README.md +5 -4
- package/README.zh-CN.md +5 -4
- package/dashboard/dist/assets/{AchievementBadge-D5sRTMmp.js → AchievementBadge-WGbdoboF.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-CaiCfchd.js → AchievementsPage-BvybbsXv.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-D6dTxdUd.js → AchievementsSection-ChNzTFSR.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-C0UrWb9m.js → BadgeDetailModal-DhI9Ejz3.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-CgpBB5FZ.js → BotAnimated-Bch0Yxej.js} +1 -1
- package/dashboard/dist/assets/{Card-CuwBiIYz.js → Card-DWdL7d1T.js} +1 -1
- package/dashboard/dist/assets/{CloudCheckoutPage-BNCnBR0h.js → CloudCheckoutPage-BjGoOz6P.js} +1 -1
- package/dashboard/dist/assets/{CloudPage-CuYXUQnv.js → CloudPage-Btb6a97x.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-CV6a_7yK.js → CommandPalette-DwP3k7LE.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-Dw-64Tig.js → CommunityStatsModal-mzqLxpwE.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-CoFO1Nas.js → ConfirmModal-Dm76q4EX.js} +1 -1
- package/dashboard/dist/assets/{Controls-DB9eQ_rg.js → Controls-ClZjOBoD.js} +1 -1
- package/dashboard/dist/assets/DashboardPage-Doc8f4NL.js +1 -0
- package/dashboard/dist/assets/{DateRangePopover-DgZEnn4X.js → DateRangePopover-vfTUePPg.js} +1 -1
- package/dashboard/dist/assets/DevicePage-Jjg5q0C2.js +1 -0
- package/dashboard/dist/assets/{DialogClose-BR9QrB34.js → DialogClose-Ci5mPZ6X.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-Di5ev5K6.js +1 -0
- package/dashboard/dist/assets/DialogTitle-DI21oZxH.js +1 -0
- package/dashboard/dist/assets/{ElectricLogo-nrlBy-l9.js → ElectricLogo-DfeZBwPZ.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-CyJJ3l_P.js → FadeIn-C7SIILTc.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-C7BX7loS.js → HeaderGithubStar-RStLbadn.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-KGluh6sO.js → HoverTooltip-DI7uAp1_.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-Ci2ybTDg.js → IpCheckPage-DTZNTJVh.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-C9N17IpV.js → LandingPage-Df5scNlw.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardPage-Cco7iFen.js → LeaderboardPage-Ck3SqtnI.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProBadge-tmm5NN8p.js → LeaderboardProBadge-CGx6m7R6.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfileModal-WhjsvZ9Q.js → LeaderboardProfileModal-DuP7rgfv.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-Ba3oVu5z.js → LeaderboardProfilePage-B5qUJkZX.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-CUa46XJy.js → LimitsPage-Bv_Lcvcz.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-BSewrWlY.js → LocalOnlyNotice-Cg4y02JM.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-CUWn0Tz4.js → LoginCard-Wz0uJKXx.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-UKk3xCsV.js → LoginModal-C13v_Gs0.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-BhxiWgc5.js → LoginPage-COH9xXtd.js} +1 -1
- package/dashboard/dist/assets/{PetPage-BRyxC-Iu.js → PetPage-BWCnURub.js} +2 -2
- package/dashboard/dist/assets/PopoverPopup-Dw811N-_.js +1 -0
- package/dashboard/dist/assets/{ProviderIcon-htNu1RTN.js → ProviderIcon-CHxhYzK-.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-DawMNiM1.js → ResetPasswordPage-Ll6jshqg.js} +1 -1
- package/dashboard/dist/assets/Select-BbooQ3sa.js +1 -0
- package/dashboard/dist/assets/SelectItemText-DBiW70wi.js +1 -0
- package/dashboard/dist/assets/{SelfHostPage-BMCMrt5I.js → SelfHostPage-BMHJxz5q.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-e_7xC0RY.js → ServiceStatusPage-D6LH0ac-.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-Xr2FJa6E.js → SessionsPage-Mrer6v17.js} +2 -2
- package/dashboard/dist/assets/SettingsPage-D_7P_y_l.js +5 -0
- package/dashboard/dist/assets/SkillsPage-Ka7xm-1v.js +1 -0
- package/dashboard/dist/assets/{TokenGalaxy--6ijNK6P.js → TokenGalaxy-BEURDPFq.js} +1 -1
- package/dashboard/dist/assets/TooltipPopup-ZiuFFARC.js +1 -0
- package/dashboard/dist/assets/{TrendMonitor-DjIZyzxp.js → TrendMonitor-H73lyGEf.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-CzCqGbb4.js → WidgetsPage-_7iCvyTk.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-B0RMtpvr.js → WrappedPage-DEXNb6-k.js} +1 -1
- package/dashboard/dist/assets/__vite-browser-external-BkNP5Ecj.js +45 -0
- package/dashboard/dist/assets/{agent-logos-CSlyeabY.js → agent-logos-OVBc_0cF.js} +1 -1
- package/dashboard/dist/assets/{arrow-left-DaqMiXTO.js → arrow-left-DDphsclm.js} +1 -1
- package/dashboard/dist/assets/{arrow-right-B1p9R8vH.js → arrow-right-B1aNq0Ki.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-CK_9HuEh.js → arrow-up-right-yTM9azz_.js} +1 -1
- package/dashboard/dist/assets/{calendar-days-BVyCx4dJ.js → calendar-days-D22ithNF.js} +1 -1
- package/dashboard/dist/assets/{check-DIfCVsPJ.js → check-Dk7tknvE.js} +1 -1
- package/dashboard/dist/assets/{chevron-down-DrWxRBXX.js → chevron-down-DdyEzQ4f.js} +1 -1
- package/dashboard/dist/assets/{cloud-checkout-DOxBG7oS.js → cloud-checkout-BJjbipnH.js} +1 -1
- package/dashboard/dist/assets/composite-DEqP00XV.js +1 -0
- package/dashboard/dist/assets/{download-CEXAmejX.js → download-C9ixNyzn.js} +1 -1
- package/dashboard/dist/assets/{external-link-tDBZy6QP.js → external-link-DL4HSsLw.js} +1 -1
- package/dashboard/dist/assets/{flame-86y9Xf0g.js → flame-DqNH65nB.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-BNN9k5mV.js → format-tokens-CyVjKz4I.js} +1 -1
- package/dashboard/dist/assets/{gift-BEOvq2zc.js → gift-ChJQQe_G.js} +1 -1
- package/dashboard/dist/assets/{host-mode-UjbNKxFB.js → host-mode-DpUDpoOz.js} +1 -1
- package/dashboard/dist/assets/{icons-DJmZD0si.js → icons-CJiHeOaU.js} +1 -1
- package/dashboard/dist/assets/{index-BwUwnqE0.js → index-72LQsAaf.js} +1 -1
- package/dashboard/dist/assets/{index-B8kizz-s.js → index-BucEg3fu.js} +1 -1
- package/dashboard/dist/assets/{index-Ci3CZ-2g.js → index-CrFlTSra.js} +1 -1
- package/dashboard/dist/assets/{info-ynVg8RzK.js → info-DReYqxC7.js} +1 -1
- package/dashboard/dist/assets/{link-2-B9SLPcBg.js → link-2-kIZbhK2S.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-FTE9k7Sk.js → loader-circle-1An-X85y.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-B4babCm_.js → modifiers.esm-FoVheEKT.js} +1 -1
- package/dashboard/dist/assets/module-DOCYJaxl.js +3 -0
- package/dashboard/dist/assets/{monitor-smartphone-CNzgbo-M.js → monitor-smartphone-Ch6lr_P2.js} +1 -1
- package/dashboard/dist/assets/{play-9W4Ua21o.js → play-CbJ6ZF1K.js} +1 -1
- package/dashboard/dist/assets/{plus-BA85lApI.js → plus-C-5ID3a9.js} +1 -1
- package/dashboard/dist/assets/{provider-display-D1ETarsz.js → provider-display-BFzRtoL2.js} +2 -2
- package/dashboard/dist/assets/{react-VsHxeqq7.js → react-D4CR_fLQ.js} +1 -1
- package/dashboard/dist/assets/safePolygon-DP21Ibqj.js +1 -0
- package/dashboard/dist/assets/{search-Svi--7CN.js → search-CaznkY7U.js} +1 -1
- package/dashboard/dist/assets/{server-CgHdX6TE.js → server-CUOmC6xZ.js} +1 -1
- package/dashboard/dist/assets/{shield-check-Cjv-T4aT.js → shield-check-Dr2KBMmA.js} +1 -1
- package/dashboard/dist/assets/{skills-api-Cn8hEYL4.js → skills-api-BAz_fblS.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-g3UcmvFU.js → sortable.esm-knEfm5hE.js} +1 -1
- package/dashboard/dist/assets/store-CL97jcij.js +1 -0
- package/dashboard/dist/assets/{terminal-o0jvDfI0.js → terminal-EjFyreWj.js} +1 -1
- package/dashboard/dist/assets/{trash-0cqpTyN6.js → trash-ByqaZXkQ.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-BFgORaJS.js → use-community-stats-Bs4RDJZE.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-9eZP_jA5.js → use-latest-request-guard-CG3Ir1cP.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-BjO3hsmw.js → use-limits-display-prefs-tACgCxeR.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-YKn-htVo.js → use-native-settings-CWjXyJXi.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-D5h4UwhF.js → use-ordered-list-DsuC9JQa.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-CwFguzkm.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-BShLuIbk.js → use-session-efficiency-pref-NeoJE8BL.js} +1 -1
- package/dashboard/dist/assets/{use-transform-DVZHJPVD.js → use-transform-BWGKOsya.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-BhJbW76K.js → use-usage-limits-CFWFNwHS.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-BmEbDVaa.js → useCurrency-B_C4_P5x.js} +1 -1
- package/dashboard/dist/assets/usePositioner-B0L5_o38.js +1 -0
- package/dashboard/dist/assets/{useTokenFormat-CBlBP2aA.js → useTokenFormat-DH1GWHSU.js} +1 -1
- package/dashboard/dist/assets/{zap-cML2-Nx4.js → zap-Chv1p7pS.js} +1 -1
- package/dashboard/dist/index.html +13 -11
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/llms.txt +3 -2
- package/dashboard/dist/share.html +1 -1
- package/package.json +3 -3
- package/src/commands/doctor.js +15 -1
- package/src/commands/init.js +9 -0
- package/src/commands/status.js +21 -3
- package/src/commands/sync.js +59 -4
- package/src/lib/cloud-device-token.js +77 -10
- package/src/lib/cloud-usage-reset.js +115 -0
- package/src/lib/diagnostics.js +5 -2
- package/src/lib/kimi-model-repair.js +173 -0
- package/src/lib/kimi-session.js +216 -0
- package/src/lib/local-api.js +46 -1
- package/src/lib/pricing/index.js +2 -1
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/qoder-cn-cli-limits.js +9 -7
- package/src/lib/qoder-limits.js +45 -15
- package/src/lib/rollout.js +408 -4
- package/src/lib/session-analytics.js +47 -9
- package/dashboard/dist/assets/DashboardPage-sZKijU6h.js +0 -1
- package/dashboard/dist/assets/DevicePage-YCnJnsmz.js +0 -1
- package/dashboard/dist/assets/DialogDescription-Cx1R3WT7.js +0 -1
- package/dashboard/dist/assets/DialogTitle-ck8f1qeP.js +0 -1
- package/dashboard/dist/assets/PopoverPopup-D-Tpl294.js +0 -1
- package/dashboard/dist/assets/Select-6WChWtqK.js +0 -1
- package/dashboard/dist/assets/SelectItemText-DRmxMwo5.js +0 -1
- package/dashboard/dist/assets/SettingsPage-Bxq2Mhxz.js +0 -5
- package/dashboard/dist/assets/SkillsPage-CEIBW63c.js +0 -1
- package/dashboard/dist/assets/TooltipPopup--TLYUg0c.js +0 -1
- package/dashboard/dist/assets/__vite-browser-external-BsOjTB8Q.js +0 -45
- package/dashboard/dist/assets/composite-W1R1XwoE.js +0 -1
- package/dashboard/dist/assets/module-BNnaKM__.js +0 -3
- package/dashboard/dist/assets/safePolygon-CCDOsx-z.js +0 -1
- package/dashboard/dist/assets/store-D2gn9bSV.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-C83gmg0F.js +0 -1
- package/dashboard/dist/assets/usePositioner-nY0YNa-6.js +0 -1
package/src/commands/doctor.js
CHANGED
|
@@ -5,6 +5,7 @@ const { readJsonStrict, writeFileAtomic, chmod600IfPossible } = require("../lib/
|
|
|
5
5
|
const { resolveTrackerPaths } = require("../lib/tracker-paths");
|
|
6
6
|
const { collectTrackerDiagnostics } = require("../lib/diagnostics");
|
|
7
7
|
const { resolveRuntimeConfig } = require("../lib/runtime-config");
|
|
8
|
+
const { resolveDeviceTokenPresence } = require("../lib/cloud-device-token");
|
|
8
9
|
const { buildDoctorReport } = require("../lib/doctor");
|
|
9
10
|
|
|
10
11
|
async function cmdDoctor(argv = []) {
|
|
@@ -16,11 +17,24 @@ async function cmdDoctor(argv = []) {
|
|
|
16
17
|
const configStatus = await readJsonStrict(configPath);
|
|
17
18
|
const config =
|
|
18
19
|
configStatus.status === "ok" && isPlainObject(configStatus.value) ? configStatus.value : {};
|
|
19
|
-
|
|
20
|
+
let runtime = resolveRuntimeConfig({
|
|
20
21
|
cli: { baseUrl: opts.baseUrl },
|
|
21
22
|
config,
|
|
22
23
|
env: process.env,
|
|
23
24
|
});
|
|
25
|
+
const deviceTokenPresence = resolveDeviceTokenPresence({
|
|
26
|
+
trackerDir,
|
|
27
|
+
config,
|
|
28
|
+
runtime,
|
|
29
|
+
});
|
|
30
|
+
if (deviceTokenPresence.set && !(typeof runtime.deviceToken === "string" && runtime.deviceToken.trim())) {
|
|
31
|
+
// Sentinel only — doctor reports set/unset and never uses the value for auth.
|
|
32
|
+
runtime = {
|
|
33
|
+
...runtime,
|
|
34
|
+
deviceToken: "present",
|
|
35
|
+
sources: { ...(runtime.sources || {}), deviceToken: deviceTokenPresence.source },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
24
38
|
const diagnostics = await collectTrackerDiagnostics({ home });
|
|
25
39
|
const cliPath = process.argv[1] ? path.resolve(process.argv[1]) : null;
|
|
26
40
|
|
package/src/commands/init.js
CHANGED
|
@@ -79,6 +79,7 @@ const {
|
|
|
79
79
|
resolveAnythingllmDbPath,
|
|
80
80
|
resolveDevinDbPath,
|
|
81
81
|
resolveReasonixHome,
|
|
82
|
+
resolveMuseHome,
|
|
82
83
|
resolveTraeStoragePath,
|
|
83
84
|
} = require("../lib/rollout");
|
|
84
85
|
const { resolveRuntimeConfig, DEFAULT_BASE_URL, normalizeInstanceBaseUrl, assertInsforgeRuntime,
|
|
@@ -141,6 +142,7 @@ const SUPPORTED_PROVIDERS = [
|
|
|
141
142
|
"Prime Agent",
|
|
142
143
|
"Craft Agents",
|
|
143
144
|
"Reasonix",
|
|
145
|
+
"Muse Code",
|
|
144
146
|
"Kilo CLI",
|
|
145
147
|
"Kilo Code",
|
|
146
148
|
"Roo Code",
|
|
@@ -882,6 +884,13 @@ async function applyIntegrationSetup({
|
|
|
882
884
|
}
|
|
883
885
|
}
|
|
884
886
|
|
|
887
|
+
// Muse Code: passive reader of ~/.local/share/muse/sessions/**/session.jsonl.
|
|
888
|
+
{
|
|
889
|
+
if (fssync.existsSync(path.join(resolveMuseHome(process.env), "sessions"))) {
|
|
890
|
+
summary.push({ label: "Muse Code", status: "detected", detail: "Passive session reader (no hook needed)" });
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
885
894
|
// International TRAE usage is read locally; no hook or vendor login is needed.
|
|
886
895
|
{
|
|
887
896
|
const { resolveTraeDbPaths } = require("../lib/trae-db");
|
package/src/commands/status.js
CHANGED
|
@@ -47,6 +47,7 @@ const {
|
|
|
47
47
|
probeOpenclawSessionPluginState,
|
|
48
48
|
} = require("../lib/openclaw-session-plugin");
|
|
49
49
|
const { resolveTrackerPaths } = require("../lib/tracker-paths");
|
|
50
|
+
const { resolveDeviceTokenPresence } = require("../lib/cloud-device-token");
|
|
50
51
|
const {
|
|
51
52
|
resolveKimiWireFiles,
|
|
52
53
|
resolveKimiCodeWireFiles,
|
|
@@ -74,6 +75,8 @@ const {
|
|
|
74
75
|
resolveCraftConfigDir,
|
|
75
76
|
resolveReasonixHome,
|
|
76
77
|
resolveReasonixTelemetryFiles,
|
|
78
|
+
resolveMuseHome,
|
|
79
|
+
resolveMuseSessionFiles,
|
|
77
80
|
resolveKilocodeTaskFiles,
|
|
78
81
|
resolveRoocodeTaskFiles,
|
|
79
82
|
resolveClineSessionFilesWithStatus,
|
|
@@ -552,6 +555,10 @@ async function cmdStatus(argv = []) {
|
|
|
552
555
|
const reasonixInstalled = Boolean(reasonixHome && fssync.existsSync(reasonixHome));
|
|
553
556
|
const reasonixFiles = reasonixInstalled ? resolveReasonixTelemetryFiles(process.env) : [];
|
|
554
557
|
|
|
558
|
+
// Muse Code (Meta) — passive scan of per-session session.jsonl logs.
|
|
559
|
+
const museInstalled = fssync.existsSync(path.join(resolveMuseHome(process.env), "sessions"));
|
|
560
|
+
const museFiles = museInstalled ? resolveMuseSessionFiles(process.env) : [];
|
|
561
|
+
|
|
555
562
|
// Kilo CLI (kilo.ai @kilocode/plugin) — passive scan of kilo.db.
|
|
556
563
|
const xdgDataHome = process.env.XDG_DATA_HOME || path.join(home, ".local", "share");
|
|
557
564
|
const kiloHome = process.env.KILO_HOME || path.join(xdgDataHome, "kilo");
|
|
@@ -1036,12 +1043,17 @@ async function cmdStatus(argv = []) {
|
|
|
1036
1043
|
},
|
|
1037
1044
|
});
|
|
1038
1045
|
|
|
1046
|
+
const deviceTokenPresence = resolveDeviceTokenPresence({
|
|
1047
|
+
trackerDir,
|
|
1048
|
+
config: config || {},
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1039
1051
|
if (opts.json || opts.light) {
|
|
1040
1052
|
const summary = {
|
|
1041
1053
|
version: pkg.version,
|
|
1042
1054
|
generated_at: new Date().toISOString(),
|
|
1043
1055
|
base_url: config?.baseUrl || null,
|
|
1044
|
-
device_token_set:
|
|
1056
|
+
device_token_set: deviceTokenPresence.set,
|
|
1045
1057
|
queue: {
|
|
1046
1058
|
pending_bytes: pendingBytes,
|
|
1047
1059
|
size_bytes: queueSize,
|
|
@@ -1136,6 +1148,9 @@ async function cmdStatus(argv = []) {
|
|
|
1136
1148
|
reasonix: reasonixInstalled
|
|
1137
1149
|
? { installed: true, files: reasonixFiles.length }
|
|
1138
1150
|
: { installed: false },
|
|
1151
|
+
muse: museInstalled
|
|
1152
|
+
? { installed: true, files: museFiles.length }
|
|
1153
|
+
: { installed: false },
|
|
1139
1154
|
anythingllm: anythingllmInstalled
|
|
1140
1155
|
? { installed: true, detail: anythingllmDbPath }
|
|
1141
1156
|
: { installed: false },
|
|
@@ -1271,7 +1286,7 @@ async function cmdStatus(argv = []) {
|
|
|
1271
1286
|
`TokenTracker v${pkg.version}`,
|
|
1272
1287
|
"Status:",
|
|
1273
1288
|
`- Base URL: ${config?.baseUrl || "unset"}`,
|
|
1274
|
-
`- Device token: ${
|
|
1289
|
+
`- Device token: ${deviceTokenPresence.set ? "set" : "unset"}`,
|
|
1275
1290
|
`- Queue: ${pendingBytes} bytes pending`,
|
|
1276
1291
|
`- Last parse: ${cursors?.updatedAt || "never"}`,
|
|
1277
1292
|
`- Last notify: ${lastNotify || "never"}`,
|
|
@@ -1300,7 +1315,7 @@ async function cmdStatus(argv = []) {
|
|
|
1300
1315
|
? `- Kimi Code: passive reader (${kimiWireFiles.length + kimiCodeWireFiles.length} wire.jsonl file${(kimiWireFiles.length + kimiCodeWireFiles.length) !== 1 ? "s" : ""} found, directories: ${kimiActive.join(" | ") || "none"})`
|
|
1301
1316
|
: null,
|
|
1302
1317
|
kiroCliInstalled
|
|
1303
|
-
? `- Kiro CLI: passive reader (${kiroCliFileCount} session file${kiroCliFileCount !== 1 ? "s" : ""} found, SQLite ${kiroCliDbFound ? "found" : "not found"}, installs: ${kiroCliActive.join(" | ")};
|
|
1318
|
+
? `- Kiro CLI: passive reader (${kiroCliFileCount} session file${kiroCliFileCount !== 1 ? "s" : ""} found, SQLite ${kiroCliDbFound ? "found" : "not found"}, installs: ${kiroCliActive.join(" | ")}; missing token counts use new-text estimates at ~4 chars/token, excluding re-sent history, system context and cache usage; model-rate costs are separate from Kiro credit charges; merged under 'kiro')`
|
|
1304
1319
|
: null,
|
|
1305
1320
|
kiroIdeInstalled
|
|
1306
1321
|
? `- Kiro IDE: passive reader (${kiroIdeActive.join(" | ")})`
|
|
@@ -1335,6 +1350,9 @@ async function cmdStatus(argv = []) {
|
|
|
1335
1350
|
reasonixInstalled
|
|
1336
1351
|
? `- Reasonix: passive reader (${reasonixFiles.length} telemetry file${reasonixFiles.length !== 1 ? "s" : ""} found)`
|
|
1337
1352
|
: null,
|
|
1353
|
+
museInstalled
|
|
1354
|
+
? `- Muse Code: passive reader (${museFiles.length} session jsonl file${museFiles.length !== 1 ? "s" : ""} found)`
|
|
1355
|
+
: null,
|
|
1338
1356
|
anythingllmInstalled
|
|
1339
1357
|
? `- AnythingLLM Desktop: passive reader (${anythingllmDbPath})`
|
|
1340
1358
|
: null,
|
package/src/commands/sync.js
CHANGED
|
@@ -5,6 +5,8 @@ const fssync = require("node:fs");
|
|
|
5
5
|
const cp = require("node:child_process");
|
|
6
6
|
const readline = require("node:readline");
|
|
7
7
|
const { functionUrlFor, fetchFunctionResponse } = require("../lib/function-url");
|
|
8
|
+
const { cloudUsageResetBlocksUpload, finishCloudDeviceUsageReset } = require("../lib/cloud-usage-reset");
|
|
9
|
+
const { KIMI_MODEL_REPAIR_KEY, repairKimiModelAttribution } = require("../lib/kimi-model-repair");
|
|
8
10
|
|
|
9
11
|
const { resolveInstallPaths, resolveZcodeNativeDbPath, resolveMimoNativeDbPath, ensureFlatCursor } = require("../lib/install-resolver");
|
|
10
12
|
const { multiInstallParse, mergeBothFileSources } = require("../lib/multi-install-parser");
|
|
@@ -100,6 +102,8 @@ const {
|
|
|
100
102
|
parseCraftIncremental,
|
|
101
103
|
resolveReasonixTelemetryFiles,
|
|
102
104
|
parseReasonixIncremental,
|
|
105
|
+
resolveMuseSessionFiles,
|
|
106
|
+
parseMuseIncremental,
|
|
103
107
|
resolveGrokBuildSessions,
|
|
104
108
|
parseGrokBuildIncremental,
|
|
105
109
|
resolveAntigravityDbPath,
|
|
@@ -347,6 +351,7 @@ const AUTO_SYNC_SOURCES = new Set([
|
|
|
347
351
|
"lmstudio",
|
|
348
352
|
"mimo",
|
|
349
353
|
"minimax-code",
|
|
354
|
+
"muse",
|
|
350
355
|
"omo",
|
|
351
356
|
"omp",
|
|
352
357
|
"opencode",
|
|
@@ -369,6 +374,7 @@ const BACKGROUND_AUTO_SYNC_SOURCES = new Set([
|
|
|
369
374
|
"acode",
|
|
370
375
|
"codex",
|
|
371
376
|
"every-code",
|
|
377
|
+
"muse",
|
|
372
378
|
"reasonix",
|
|
373
379
|
]);
|
|
374
380
|
|
|
@@ -542,7 +548,13 @@ async function cmdSync(argv, context = {}) {
|
|
|
542
548
|
// Hooks, native publication and detached retries must honor the saved opt-in.
|
|
543
549
|
const requiresCloudSyncPref = opts.auto || opts.background || opts.fromRetry || opts.fromNotify || opts.fromOpenclaw ||
|
|
544
550
|
Boolean(process.env.TOKENTRACKER_LOCAL_SYNC_ATTEMPT_ID);
|
|
545
|
-
|
|
551
|
+
let cloudResetRuntime = null;
|
|
552
|
+
const canUpload = () => !cloudUsageResetBlocksUpload(trackerDir, {
|
|
553
|
+
ownerId: process.env.TOKENTRACKER_CLOUD_OWNER_ID,
|
|
554
|
+
baseUrl: cloudResetRuntime?.baseUrl || process.env.TOKENTRACKER_INSFORGE_BASE_URL,
|
|
555
|
+
deviceId: process.env.TOKENTRACKER_CLOUD_DEVICE_ID,
|
|
556
|
+
}) &&
|
|
557
|
+
(!requiresCloudSyncPref || readCloudSyncEnabled(trackerDir));
|
|
546
558
|
|
|
547
559
|
await ensureDir(trackerDir);
|
|
548
560
|
if (opts.fromOpenclaw) {
|
|
@@ -2508,6 +2520,17 @@ async function cmdSync(argv, context = {}) {
|
|
|
2508
2520
|
const kimiWireFiles = sourceAllowed("kimi")
|
|
2509
2521
|
? mergeBothFileSources({ resolveFiles: resolveKimiWireFiles, env: process.env })
|
|
2510
2522
|
: [];
|
|
2523
|
+
const kimiCodeWireFiles = sourceAllowed("kimi-code")
|
|
2524
|
+
? mergeBothFileSources({ resolveFiles: resolveKimiCodeWireFiles, env: process.env })
|
|
2525
|
+
: [];
|
|
2526
|
+
if (isFullSourceScan && sourceAllowed("kimi") && sourceAllowed("kimi-code")) {
|
|
2527
|
+
await repairKimiModelAttribution({
|
|
2528
|
+
cursors, queuePath, queueStatePath,
|
|
2529
|
+
legacyFiles: kimiWireFiles, officialFiles: kimiCodeWireFiles,
|
|
2530
|
+
parseLegacy: parseKimiIncremental, parseOfficial: parseKimiCodeIncremental,
|
|
2531
|
+
env: process.env,
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2511
2534
|
if (kimiWireFiles.length > 0) {
|
|
2512
2535
|
if (progress?.enabled) {
|
|
2513
2536
|
progress.start(`Parsing Kimi Code ${renderBar(0)} | buckets 0`);
|
|
@@ -2533,9 +2556,6 @@ async function cmdSync(argv, context = {}) {
|
|
|
2533
2556
|
|
|
2534
2557
|
// ── Kimi Code official (@moonshot-ai/kimi-code, ~/.kimi-code) ──
|
|
2535
2558
|
let kimiCodeResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
2536
|
-
const kimiCodeWireFiles = sourceAllowed("kimi-code")
|
|
2537
|
-
? mergeBothFileSources({ resolveFiles: resolveKimiCodeWireFiles, env: process.env })
|
|
2538
|
-
: [];
|
|
2539
2559
|
if (kimiCodeWireFiles.length > 0) {
|
|
2540
2560
|
if (progress?.enabled) {
|
|
2541
2561
|
progress.start(`Parsing Kimi Code (official) ${renderBar(0)} | buckets 0`);
|
|
@@ -2899,6 +2919,25 @@ async function cmdSync(argv, context = {}) {
|
|
|
2899
2919
|
}
|
|
2900
2920
|
}
|
|
2901
2921
|
|
|
2922
|
+
// Muse Code (Meta) — usage counts from per-session session.jsonl logs.
|
|
2923
|
+
let museResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
2924
|
+
const museFiles = sourceAllowed("muse")
|
|
2925
|
+
? mergeBothFileSources({ resolveFiles: resolveMuseSessionFiles, env: process.env })
|
|
2926
|
+
: [];
|
|
2927
|
+
if (museFiles.length > 0) {
|
|
2928
|
+
try {
|
|
2929
|
+
museResult = await parseMuseIncremental({
|
|
2930
|
+
sessionFiles: museFiles,
|
|
2931
|
+
cursors,
|
|
2932
|
+
queuePath,
|
|
2933
|
+
env: process.env,
|
|
2934
|
+
onProgress: makeProviderProgress("Muse"),
|
|
2935
|
+
});
|
|
2936
|
+
} catch (err) {
|
|
2937
|
+
warnProviderParseFailure("Muse", err, opts);
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2902
2941
|
// ── Grok Build (xAI) ──
|
|
2903
2942
|
let grokResult = {
|
|
2904
2943
|
recordsProcessed: 0,
|
|
@@ -3357,6 +3396,7 @@ async function cmdSync(argv, context = {}) {
|
|
|
3357
3396
|
minimaxCodeResult.recordsProcessed +
|
|
3358
3397
|
craftResult.recordsProcessed +
|
|
3359
3398
|
reasonixResult.recordsProcessed +
|
|
3399
|
+
museResult.recordsProcessed +
|
|
3360
3400
|
grokResult.recordsProcessed +
|
|
3361
3401
|
copilotResult.recordsProcessed +
|
|
3362
3402
|
lmstudioResult.recordsProcessed +
|
|
@@ -3403,6 +3443,7 @@ async function cmdSync(argv, context = {}) {
|
|
|
3403
3443
|
minimaxCodeResult.bucketsQueued +
|
|
3404
3444
|
craftResult.bucketsQueued +
|
|
3405
3445
|
reasonixResult.bucketsQueued +
|
|
3446
|
+
museResult.bucketsQueued +
|
|
3406
3447
|
grokResult.bucketsQueued +
|
|
3407
3448
|
copilotResult.bucketsQueued +
|
|
3408
3449
|
lmstudioResult.bucketsQueued +
|
|
@@ -3434,6 +3475,7 @@ async function cmdSync(argv, context = {}) {
|
|
|
3434
3475
|
!codexColdAuditDue &&
|
|
3435
3476
|
!codexFallbackRetryRan &&
|
|
3436
3477
|
!grokHookSignalConsumed &&
|
|
3478
|
+
!cursors.migrations?.[KIMI_MODEL_REPAIR_KEY]?.pendingCompaction &&
|
|
3437
3479
|
!opts.repairGrok;
|
|
3438
3480
|
|
|
3439
3481
|
if (syncDiagnostics) {
|
|
@@ -3458,6 +3500,12 @@ async function cmdSync(argv, context = {}) {
|
|
|
3458
3500
|
deferredCodexAuditSyncs + 1,
|
|
3459
3501
|
);
|
|
3460
3502
|
}
|
|
3503
|
+
if (cursors.migrations?.[KIMI_MODEL_REPAIR_KEY]?.pendingCompaction) {
|
|
3504
|
+
const state = (await readJson(queueStatePath)) || { offset: 0 };
|
|
3505
|
+
await compactPendingQueue({ queuePath, offset: Number(state.offset || 0), minPendingBytes: 0 });
|
|
3506
|
+
cursors.migrations[KIMI_MODEL_REPAIR_KEY].pendingCompaction = false;
|
|
3507
|
+
await cursorStore.commit(cursors);
|
|
3508
|
+
}
|
|
3461
3509
|
if (grokHookSignalConsumed && grokHookSignalPath) {
|
|
3462
3510
|
await fs.unlink(grokHookSignalPath).catch(() => {});
|
|
3463
3511
|
}
|
|
@@ -3490,6 +3538,7 @@ async function cmdSync(argv, context = {}) {
|
|
|
3490
3538
|
const runtime = Object.hasOwn(process.env, "TOKENTRACKER_LOCAL_SYNC_DEVICE_TOKEN")
|
|
3491
3539
|
? { ...resolvedRuntime, deviceToken: resolvedRuntime.configurationError ? null : process.env.TOKENTRACKER_LOCAL_SYNC_DEVICE_TOKEN }
|
|
3492
3540
|
: resolvedRuntime;
|
|
3541
|
+
cloudResetRuntime = runtime;
|
|
3493
3542
|
|
|
3494
3543
|
let uploadResult = { inserted: 0, skipped: 0 };
|
|
3495
3544
|
let uploadAttempted = false;
|
|
@@ -3658,6 +3707,12 @@ async function cmdSync(argv, context = {}) {
|
|
|
3658
3707
|
// and can keep auto retry alive even after cloud sync has drained.
|
|
3659
3708
|
const pendingBytes = Math.max(0, queueSize - Number(afterState.offset || 0));
|
|
3660
3709
|
|
|
3710
|
+
if (pendingBytes === 0 && canUpload() && runtime.deviceToken) {
|
|
3711
|
+
await finishCloudDeviceUsageReset({ trackerDir, ownerId: process.env.TOKENTRACKER_CLOUD_OWNER_ID,
|
|
3712
|
+
baseUrl: runtime.baseUrl, anonKey: runtime.anonKey, deviceToken: runtime.deviceToken,
|
|
3713
|
+
deviceId: process.env.TOKENTRACKER_CLOUD_DEVICE_ID });
|
|
3714
|
+
}
|
|
3715
|
+
|
|
3661
3716
|
if (pendingBytes <= 0 || !canUpload()) {
|
|
3662
3717
|
await clearAutoRetry(trackerDir);
|
|
3663
3718
|
} else if (opts.auto && uploadAttempted && !opts.publishAccount) {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
const fs = require("node:fs");
|
|
2
2
|
const path = require("node:path");
|
|
3
3
|
const crypto = require("node:crypto");
|
|
4
|
+
const {
|
|
5
|
+
resolveRuntimeConfig,
|
|
6
|
+
normalizeInstanceBaseUrl,
|
|
7
|
+
} = require("./runtime-config");
|
|
4
8
|
|
|
5
9
|
function validBinding(binding) {
|
|
6
10
|
return ["userId", "baseUrl", "machineId"].every(
|
|
@@ -8,6 +12,27 @@ function validBinding(binding) {
|
|
|
8
12
|
);
|
|
9
13
|
}
|
|
10
14
|
|
|
15
|
+
function readCloudDeviceTokenEntry(filePath) {
|
|
16
|
+
let fd;
|
|
17
|
+
try {
|
|
18
|
+
fd = fs.openSync(filePath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
|
19
|
+
if (!fs.fstatSync(fd).isFile()) return null;
|
|
20
|
+
fs.fchmodSync(fd, 0o600);
|
|
21
|
+
const data = JSON.parse(fs.readFileSync(fd, "utf8"));
|
|
22
|
+
if (data?.version === 1 && validBinding(data) && typeof data.token === "string" && data.token) {
|
|
23
|
+
return {
|
|
24
|
+
version: 1,
|
|
25
|
+
userId: data.userId,
|
|
26
|
+
baseUrl: data.baseUrl,
|
|
27
|
+
machineId: data.machineId,
|
|
28
|
+
token: data.token,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
} catch { /* missing or invalid cache requires ordinary issuance */ }
|
|
32
|
+
finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
11
36
|
// A successful auth refresh supplies the binding. This file never imports a
|
|
12
37
|
// CLI config token, whose owner may belong to another signed-in account.
|
|
13
38
|
function createCloudDeviceTokenStore(filePath) {
|
|
@@ -17,15 +42,7 @@ function createCloudDeviceTokenStore(filePath) {
|
|
|
17
42
|
function load() {
|
|
18
43
|
if (loaded) return entry;
|
|
19
44
|
loaded = true;
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
fd = fs.openSync(filePath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
|
23
|
-
if (!fs.fstatSync(fd).isFile()) return null;
|
|
24
|
-
fs.fchmodSync(fd, 0o600);
|
|
25
|
-
const data = JSON.parse(fs.readFileSync(fd, "utf8"));
|
|
26
|
-
if (data?.version === 1 && validBinding(data) && typeof data.token === "string" && data.token) entry = data;
|
|
27
|
-
} catch { /* missing or invalid cache requires ordinary issuance */ }
|
|
28
|
-
finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
45
|
+
entry = readCloudDeviceTokenEntry(filePath);
|
|
29
46
|
return entry;
|
|
30
47
|
}
|
|
31
48
|
|
|
@@ -71,4 +88,54 @@ function createCloudDeviceTokenStore(filePath) {
|
|
|
71
88
|
};
|
|
72
89
|
}
|
|
73
90
|
|
|
74
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Presence for status/doctor/diagnostics — same stores upload uses.
|
|
93
|
+
* Prefer config/env/cli (resolveRuntimeConfig); else cloud-device-token.json
|
|
94
|
+
* when baseUrl (+ machineId when known) match. Never clears the store.
|
|
95
|
+
*/
|
|
96
|
+
function resolveDeviceTokenPresence({
|
|
97
|
+
trackerDir,
|
|
98
|
+
config = {},
|
|
99
|
+
runtime = null,
|
|
100
|
+
env = process.env,
|
|
101
|
+
} = {}) {
|
|
102
|
+
const resolved = runtime || resolveRuntimeConfig({ config, env });
|
|
103
|
+
if (typeof resolved?.deviceToken === "string" && resolved.deviceToken.trim()) {
|
|
104
|
+
return {
|
|
105
|
+
set: true,
|
|
106
|
+
source: resolved.sources?.deviceToken || "config",
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!trackerDir || typeof trackerDir !== "string") {
|
|
111
|
+
return { set: false, source: null };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const entry = readCloudDeviceTokenEntry(path.join(trackerDir, "cloud-device-token.json"));
|
|
115
|
+
if (!entry) return { set: false, source: null };
|
|
116
|
+
|
|
117
|
+
const expectedBase =
|
|
118
|
+
normalizeInstanceBaseUrl(resolved?.baseUrl) ||
|
|
119
|
+
(typeof resolved?.baseUrl === "string" ? resolved.baseUrl.trim() : "") ||
|
|
120
|
+
normalizeInstanceBaseUrl(config.baseUrl) ||
|
|
121
|
+
(typeof config.baseUrl === "string" ? config.baseUrl.trim() : "");
|
|
122
|
+
if (expectedBase && entry.baseUrl !== expectedBase) {
|
|
123
|
+
return { set: false, source: null };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const expectedMachine =
|
|
127
|
+
typeof config.machineId === "string" && config.machineId.trim()
|
|
128
|
+
? config.machineId.trim()
|
|
129
|
+
: null;
|
|
130
|
+
if (expectedMachine && entry.machineId !== expectedMachine) {
|
|
131
|
+
return { set: false, source: null };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return { set: true, source: "cloud-device-token" };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = {
|
|
138
|
+
createCloudDeviceTokenStore,
|
|
139
|
+
resolveDeviceTokenPresence,
|
|
140
|
+
readCloudDeviceTokenEntry,
|
|
141
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const fsp = require("node:fs/promises");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
const crypto = require("node:crypto");
|
|
5
|
+
const { openLock, writeFileAtomic } = require("./fs");
|
|
6
|
+
const { functionUrlFor, fetchFunctionResponse } = require("./function-url");
|
|
7
|
+
|
|
8
|
+
const MARKER = "cloud-usage-reset.json";
|
|
9
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10
|
+
const failure = (code, status = 409) => Object.assign(new Error(code), { code, status });
|
|
11
|
+
const sameBase = (a, b) => String(a).replace(/\/$/, "") === String(b).replace(/\/$/, "");
|
|
12
|
+
|
|
13
|
+
function readCloudUsageReset(trackerDir) {
|
|
14
|
+
let raw;
|
|
15
|
+
try { raw = fs.readFileSync(path.join(trackerDir, MARKER), "utf8"); }
|
|
16
|
+
catch (error) { if (error.code === "ENOENT") return null; throw error; }
|
|
17
|
+
let value;
|
|
18
|
+
try { value = JSON.parse(raw); } catch { throw failure("cloud_usage_reset_invalid_state"); }
|
|
19
|
+
if (value?.version !== 1 || !UUID.test(value.operationId) || !UUID.test(value.deviceId) ||
|
|
20
|
+
!UUID.test(value.ownerId) || !/^https?:\/\//.test(value.baseUrl || "") ||
|
|
21
|
+
!["prepared", "uploading"].includes(value.phase)) throw failure("cloud_usage_reset_invalid_state");
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function cloudUsageResetBlocksUpload(trackerDir, { ownerId, baseUrl, deviceId } = {}) {
|
|
26
|
+
try {
|
|
27
|
+
const marker = readCloudUsageReset(trackerDir);
|
|
28
|
+
return Boolean(marker && (marker.phase === "prepared" || marker.ownerId !== ownerId ||
|
|
29
|
+
!sameBase(marker.baseUrl, baseUrl) || (deviceId && marker.deviceId !== deviceId)));
|
|
30
|
+
}
|
|
31
|
+
catch { return true; }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function writeMarker(trackerDir, marker) {
|
|
35
|
+
await writeFileAtomic(path.join(trackerDir, MARKER), JSON.stringify(marker) + "\n", { mode: 0o600 });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function requestReset({ baseUrl, anonKey, authorization, body, fetchImpl = fetchFunctionResponse }) {
|
|
39
|
+
const response = await fetchImpl(functionUrlFor(baseUrl, "tokentracker-device-usage-reset"), {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: { "Content-Type": "application/json", Accept: "application/json", Authorization: `Bearer ${authorization}`,
|
|
42
|
+
...(anonKey ? { apikey: anonKey } : {}) },
|
|
43
|
+
signal: AbortSignal.timeout(60_000),
|
|
44
|
+
body: JSON.stringify(body),
|
|
45
|
+
});
|
|
46
|
+
const data = await response.json().catch(() => ({}));
|
|
47
|
+
if (!response.ok || data.ok !== true) throw failure(data.code || "cloud_usage_reset_failed", response.status || 502);
|
|
48
|
+
if (data.operation_id !== body.operation_id) throw failure("cloud_usage_reset_invalid_response", 502);
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Called only after the local bridge has bound the JWT to its current device.
|
|
53
|
+
// The marker precedes the remote mutation: an interrupted request must retry the
|
|
54
|
+
// same operation, never clear already-replayed history a second time.
|
|
55
|
+
async function resetCloudDeviceUsage({ trackerDir, ownerId, deviceId, baseUrl, anonKey, accessToken,
|
|
56
|
+
isCurrent = () => true, fetchImpl }) {
|
|
57
|
+
if (!UUID.test(ownerId) || !UUID.test(deviceId) || !accessToken) throw failure("auth_required", 401);
|
|
58
|
+
const lock = await openLock(path.join(trackerDir, "sync.lock"), { quietIfLocked: true });
|
|
59
|
+
if (!lock) throw failure("cloud_usage_reset_sync_busy");
|
|
60
|
+
try {
|
|
61
|
+
if (!isCurrent()) throw failure("auth_session_changed");
|
|
62
|
+
let marker = readCloudUsageReset(trackerDir);
|
|
63
|
+
const newOperation = !marker;
|
|
64
|
+
if (marker && (marker.ownerId !== ownerId || marker.deviceId !== deviceId || !sameBase(marker.baseUrl, baseUrl))) {
|
|
65
|
+
throw failure("cloud_usage_reset_owner_changed");
|
|
66
|
+
}
|
|
67
|
+
if (marker?.phase === "uploading") return { ok: true, operation_id: marker.operationId, status: "uploading" };
|
|
68
|
+
if (!marker) {
|
|
69
|
+
marker = { version: 1, operationId: crypto.randomUUID(), ownerId, deviceId, baseUrl, phase: "prepared" };
|
|
70
|
+
await writeMarker(trackerDir, marker);
|
|
71
|
+
}
|
|
72
|
+
if (!isCurrent()) throw failure("auth_session_changed");
|
|
73
|
+
const result = await requestReset({ baseUrl, anonKey, authorization: accessToken, fetchImpl,
|
|
74
|
+
body: { action: "reset", device_id: deviceId, operation_id: marker.operationId } }).catch(async error => {
|
|
75
|
+
// A definitive first-request rejection did not erase anything. Unknown
|
|
76
|
+
// outcomes and retries retain their operation so they cannot erase twice.
|
|
77
|
+
if (newOperation && [400, 401, 403, 404, 409, 422].includes(error.status)) {
|
|
78
|
+
await fsp.unlink(path.join(trackerDir, MARKER));
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
});
|
|
82
|
+
if (result.device_id !== deviceId || !["reset", "complete"].includes(result.status)) {
|
|
83
|
+
throw failure("cloud_usage_reset_invalid_response", 502);
|
|
84
|
+
}
|
|
85
|
+
if (!isCurrent()) throw failure("auth_session_changed");
|
|
86
|
+
await writeFileAtomic(path.join(trackerDir, "queue.state.json"),
|
|
87
|
+
JSON.stringify({ offset: 0, updatedAt: new Date().toISOString() }) + "\n", { mode: 0o600 });
|
|
88
|
+
await fsp.unlink(path.join(trackerDir, "upload.throttle.json")).catch(error => { if (error.code !== "ENOENT") throw error; });
|
|
89
|
+
marker.phase = "uploading";
|
|
90
|
+
await writeMarker(trackerDir, marker);
|
|
91
|
+
return { ...result, status: "uploading" };
|
|
92
|
+
} finally { await lock.release(); }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// sync owns sync.lock here. Only its fully acknowledged queue can finish the
|
|
96
|
+
// operation; a partial batch/checkpoint or another account leaves it pending.
|
|
97
|
+
async function finishCloudDeviceUsageReset({ trackerDir, ownerId, deviceId, baseUrl, anonKey, deviceToken, fetchImpl }) {
|
|
98
|
+
const marker = readCloudUsageReset(trackerDir);
|
|
99
|
+
if (!marker || marker.phase !== "uploading" || marker.ownerId !== ownerId || (deviceId && marker.deviceId !== deviceId) || !sameBase(marker.baseUrl, baseUrl)) return false;
|
|
100
|
+
if (!deviceToken) return false;
|
|
101
|
+
const queuePath = path.join(trackerDir, "queue.jsonl");
|
|
102
|
+
const size = await fsp.stat(queuePath).then(stat => stat.size).catch(error => { if (error.code === "ENOENT") return 0; throw error; });
|
|
103
|
+
let offset;
|
|
104
|
+
try { offset = JSON.parse(await fsp.readFile(path.join(trackerDir, "queue.state.json"), "utf8")).offset; }
|
|
105
|
+
catch { throw failure("cloud_usage_reset_invalid_offset"); }
|
|
106
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || offset > size) throw failure("cloud_usage_reset_invalid_offset");
|
|
107
|
+
if (offset !== size) return false;
|
|
108
|
+
const result = await requestReset({ baseUrl, anonKey, authorization: deviceToken, fetchImpl,
|
|
109
|
+
body: { action: "refresh", operation_id: marker.operationId } });
|
|
110
|
+
if (result.status !== "complete" || result.device_id !== marker.deviceId) throw failure("cloud_usage_reset_invalid_response", 502);
|
|
111
|
+
await fsp.unlink(path.join(trackerDir, MARKER));
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = { readCloudUsageReset, cloudUsageResetBlocksUpload, resetCloudDeviceUsage, finishCloudDeviceUsageReset };
|
package/src/lib/diagnostics.js
CHANGED
|
@@ -19,6 +19,7 @@ const { probeOpenclawHookState } = require("./openclaw-hook");
|
|
|
19
19
|
const { probeOpenclawSessionPluginState } = require("./openclaw-session-plugin");
|
|
20
20
|
const { probeGrokHookState } = require("./grok-hook");
|
|
21
21
|
const { resolveTrackerPaths } = require("./tracker-paths");
|
|
22
|
+
const { resolveDeviceTokenPresence } = require("./cloud-device-token");
|
|
22
23
|
const wsl = require("./wsl-probe");
|
|
23
24
|
// TASK-011: Kiro paths inlined here to avoid pulling the ~4000-line
|
|
24
25
|
// rollout module on every `tokentracker status` / `diagnostics` call.
|
|
@@ -228,7 +229,7 @@ async function collectTrackerDiagnostics({
|
|
|
228
229
|
? { wsl_mode: wsl.getWslMode(process.env), wsl_installs: kiroWslInstalls }
|
|
229
230
|
: {}),
|
|
230
231
|
cli_approximation:
|
|
231
|
-
"Kiro CLI does not
|
|
232
|
+
"When Kiro CLI does not report token counts, tokens are estimated at ~4 chars/token from new user messages, tool outputs and assistant text. The estimate does not measure re-sent history, system context or cache usage, and can substantially undercount tool-heavy requests. contextUsage percentages are not converted to tokens because their denominator and request correspondence are not established. Dollar amounts use model rates, not Kiro credit charges; recorded usage_summary credits remain separate. Source rows use model='kiro-cli-agent' when the underlying model is unknown; known Bedrock ARNs canonicalize to their short name (e.g. claude-sonnet-4).",
|
|
232
233
|
merge_policy:
|
|
233
234
|
"Kiro IDE and Kiro CLI both emit source='kiro' in queue.jsonl so token, cost, heatmap, and leaderboard aggregations merge transparently. Use this block to distinguish sub-path contributions.",
|
|
234
235
|
},
|
|
@@ -238,7 +239,9 @@ async function collectTrackerDiagnostics({
|
|
|
238
239
|
},
|
|
239
240
|
config: {
|
|
240
241
|
base_url: typeof config?.baseUrl === "string" ? config.baseUrl : null,
|
|
241
|
-
device_token: config
|
|
242
|
+
device_token: resolveDeviceTokenPresence({ trackerDir, config: config || {} }).set
|
|
243
|
+
? "set"
|
|
244
|
+
: "unset",
|
|
242
245
|
device_id: maskId(config?.deviceId),
|
|
243
246
|
installed_at: typeof config?.installedAt === "string" ? config.installedAt : null,
|
|
244
247
|
},
|