tokentracker-cli 0.87.9 → 0.87.10
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/dashboard/dist/assets/{AchievementBadge-D3_hERkQ.js → AchievementBadge-BG9bVXuQ.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-DZGjT92g.js → AchievementsPage-DwYIajgz.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-CEzq3U3A.js → AchievementsSection-BPuhWCWC.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-D3_B8E--.js → BadgeDetailModal-BU81AHIH.js} +1 -1
- package/dashboard/dist/assets/{Card-CMXM3-lp.js → Card-CxiGV6bE.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-lQxUKRzw.js → ClawdAnimated-NHcv2_KK.js} +1 -1
- package/dashboard/dist/assets/{CommandPalette-BCGdAeFB.js → CommandPalette-BKmTa5b8.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-BwE5h2y5.js → CommunityStatsModal-nhHtSw7U.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-8uhS2ccj.js → ConfirmModal-DCicMVqg.js} +1 -1
- package/dashboard/dist/assets/{Controls-CBEQTl2X.js → Controls-CtjjJ8gt.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-UE_3tGVy.js → DashboardPage-BL-NfLIM.js} +1 -1
- package/dashboard/dist/assets/DevicePage-DuIiuci7.js +1 -0
- package/dashboard/dist/assets/{DialogClose-L_t-nrYm.js → DialogClose-8GK6rJCf.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-C9StmaUp.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-Bsft4VvL.js → DialogTitle-EBphaJIt.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-BNDdLqTu.js → FadeIn-B8099njp.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-CvNaKDbl.js → HeaderGithubStar-Cyxc4vKa.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-D5j5IpNx.js → HoverTooltip-BcXNDc20.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-Csf8gMJW.js → IpCheckPage-Bn5aOiJK.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-BVSSwM0D.js → LandingPage-BMbP2M_2.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-DXxSHqmP.js → LeaderboardAvatar-sRNGsVJE.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-DXj21BBR.js → LeaderboardPage-DUv9hCNQ.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-DbEzZGg4.js → LeaderboardProfileModal-D66DzEVL.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-DoIm3jB_.js → LeaderboardProfilePage-CWzNEHjJ.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-D_tpQrUM.js → LimitsPage-B7dTsxXt.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-DBi5iuPr.js → LocalOnlyNotice-BCR-4Ipk.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-qDIJgUX1.js → LoginCard-Dt7v1Pgi.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-D4b5p4u1.js → LoginModal-BaKS0AvW.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-CsSgItFA.js → LoginPage-J7ir_N3K.js} +1 -1
- package/dashboard/dist/assets/{PetPage-B-wElg5q.js → PetPage-uAxjE9Vs.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-9C_VarUQ.js → PopoverPopup-WRGNwhEe.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-CciEdEeR.js → ProviderIcon-BTZXNlML.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-lsI8qlzE.js → ResetPasswordPage-B_TvqHvF.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-BGonMV45.js → SegmentedControl-C1NPnzuC.js} +1 -1
- package/dashboard/dist/assets/{Select-DhRrGrDT.js → Select-DetlANRp.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-ClMYgZsa.js → SelectItemText-CdhCuD7u.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-DuzttgL7.js → ServiceStatusPage-yMTV03js.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-gZbxx0f_.js → SessionsPage-rJw-lY6d.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-NhxTrVNW.js → SettingsPage-BHIGCXa8.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-BeXojJqQ.js → SkillsPage-BZTTHrKR.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-DM49zKzo.js → TokenGalaxy-D2tFBs42.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-BfvoVNMW.js → ToolbarRootContext-CD4Dh9IV.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-Bu-dXcrV.js → TrendMonitor-mL7Z3Pu9.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-D2Wo7seE.js → WidgetsPage-Dech66hD.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-BXAwic4D.js → WrappedPage-CYaLijy2.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-BDEQk--l.js → __vite-browser-external-CcMKDoRA.js} +2 -2
- package/dashboard/dist/assets/{agent-logos-DMEEHjG7.js → agent-logos-BzFhwsm5.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-gNviKtY8.js → arrow-up-right-TrcWBi5O.js} +1 -1
- package/dashboard/dist/assets/{check-Cvg5pmYP.js → check-CKWLtHpO.js} +1 -1
- package/dashboard/dist/assets/{chevron-right-DxdaNTo8.js → chevron-right-BjL_B3tf.js} +1 -1
- package/dashboard/dist/assets/{copy-BNIJL7bf.js → copy-B5XqmEDV.js} +1 -1
- package/dashboard/dist/assets/{download-C9McJlPa.js → download-BtVO_3_3.js} +1 -1
- package/dashboard/dist/assets/{external-link-C_IU6zDK.js → external-link-CTpQdeXU.js} +1 -1
- package/dashboard/dist/assets/{flame-CII7X8Je.js → flame-CMixyq1C.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-DuQbGpPt.js → format-tokens-DOgiRUv2.js} +1 -1
- package/dashboard/dist/assets/{icons-B1YTqnxG.js → icons-DNpysD5S.js} +1 -1
- package/dashboard/dist/assets/{index-BRMxraNK.js → index-B7Zuq72U.js} +1 -1
- package/dashboard/dist/assets/{index-QtcYir6O.js → index-Dadn8f0V.js} +1 -1
- package/dashboard/dist/assets/{index-Dc9xsIu2.js → index-k5haeuyB.js} +1 -1
- package/dashboard/dist/assets/{info-CIgVqJoj.js → info-CodBJXwL.js} +1 -1
- package/dashboard/dist/assets/{limits-providers-B31NsGar.js → limits-providers-DiSK8fij.js} +1 -1
- package/dashboard/dist/assets/{link-2-DTPXzHuB.js → link-2-BE84-D1K.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-CgQSyCz8.js → loader-circle-62hGDJej.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-BL5J4aHZ.js → maximize-2-Dn0j7N4g.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-s_rzcEf2.js → modifiers.esm-D48EfMXn.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-C9ei_xde.js → monitor-smartphone-B4O-1qqp.js} +1 -1
- package/dashboard/dist/assets/{provider-display-ZgvoJi05.js → provider-display-BBlUUXou.js} +1 -1
- package/dashboard/dist/assets/{react-DhfPVGOm.js → react-B7hdp-KU.js} +1 -1
- package/dashboard/dist/assets/{search-Ba_MWO7q.js → search-DYcfh3D9.js} +1 -1
- package/dashboard/dist/assets/{skills-api-BkmJmyg1.js → skills-api-BL0Jj4TS.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-Bmd5t12E.js → sortable.esm-UJyxEfLh.js} +1 -1
- package/dashboard/dist/assets/{store-BxRQs9yc.js → store-koEJUHLx.js} +1 -1
- package/dashboard/dist/assets/{terminal-BzUwAs4g.js → terminal-CA8uz-Rp.js} +1 -1
- package/dashboard/dist/assets/{trash-2-CCprY7cu.js → trash-2-Cwdcw5RS.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-TCdPJ_ir.js → use-community-stats-BIBrzbia.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-CNF9luO6.js → use-limits-display-prefs-BCdQk1ck.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-BlTuSYJy.js → use-ordered-list-Bf9Ozum9.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-DGTFk58t.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-iHS0O_Fo.js → use-session-efficiency-pref-C83fbwfz.js} +1 -1
- package/dashboard/dist/assets/{use-transform-CcVfL3mi.js → use-transform-DnR7Iz0p.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-Xdy-Y_1Z.js → use-usage-limits-D_MtlDKv.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-IXccXbmx.js → useCurrency-DVO1i6vp.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-BEVewfZm.js → useScrollLock-CW2lrPuY.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-Cihv3Ghl.js → useTokenFormat-BFhS3XRy.js} +1 -1
- package/dashboard/dist/assets/{zap-zG2Fbb5_.js → zap-DCY5z8oI.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/commands/sync.js +186 -1
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +232 -44
- package/dashboard/dist/assets/DevicePage-B06ybHJI.js +0 -1
- package/dashboard/dist/assets/DialogDescription-B7FFtR7A.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-CfiR91Eg.js +0 -1
package/src/commands/sync.js
CHANGED
|
@@ -144,6 +144,7 @@ const ROLLOUT_CUMULATIVE_DELTA_MIGRATION_KEY = "rolloutCumulativeDeltaReparse_20
|
|
|
144
144
|
const CLAUDE_MEM_OBSERVER_REINCLUDE_KEY = "claudeMemObserverReinclude_2026_05_v3";
|
|
145
145
|
const GROK_APPEND_ONLY_REPAIR_MIGRATION_KEY = "grokAppendOnlyRepair_2026_05_v4";
|
|
146
146
|
const CODEBUDDY_LOG_JSONL_REPAIR_KEY = "codebuddyLogJsonlOverlapRepair_2026_08";
|
|
147
|
+
const WORKBUDDY_CONTEXT_USAGE_REPAIR_KEY = "workbuddyContextUsageRepair_2026_08";
|
|
147
148
|
const CLAUDE_MEM_OBSERVER_PATH_SEGMENT = "--claude-mem-observer-sessions";
|
|
148
149
|
// v1 had a cursor-format bug (wrote plain integer instead of {inode, offset,
|
|
149
150
|
// updatedAt}), which made parseClaudeIncremental reread every jsonl from
|
|
@@ -1837,6 +1838,19 @@ async function cmdSync(argv, context = {}) {
|
|
|
1837
1838
|
? mergeBothFileSources({ resolveFiles: resolveWorkbuddyProjectFiles, env: process.env })
|
|
1838
1839
|
: [];
|
|
1839
1840
|
if (sourceAllowed("workbuddy")) {
|
|
1841
|
+
if (isFullSourceScan) {
|
|
1842
|
+
try {
|
|
1843
|
+
await repairWorkbuddyContextUsage({
|
|
1844
|
+
cursors,
|
|
1845
|
+
queuePath,
|
|
1846
|
+
queueStatePath,
|
|
1847
|
+
workbuddyFiles,
|
|
1848
|
+
env: process.env,
|
|
1849
|
+
});
|
|
1850
|
+
} catch (err) {
|
|
1851
|
+
warnProviderParseFailure("WorkBuddy context-usage repair", err, opts);
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1840
1854
|
if (progress?.enabled) {
|
|
1841
1855
|
progress.start(`Parsing WorkBuddy ${renderBar(0)} | buckets 0`);
|
|
1842
1856
|
}
|
|
@@ -2762,6 +2776,8 @@ module.exports = {
|
|
|
2762
2776
|
migrateCursorUnknownBuckets,
|
|
2763
2777
|
migrateRolloutCumulativeDeltaBuckets,
|
|
2764
2778
|
repairCodebuddyLogJsonlOverlap,
|
|
2779
|
+
repairWorkbuddyContextUsage,
|
|
2780
|
+
WORKBUDDY_CONTEXT_USAGE_REPAIR_KEY,
|
|
2765
2781
|
repairCodexRescanInflation,
|
|
2766
2782
|
repairCodexForkReplayInflation,
|
|
2767
2783
|
repairCodexInterleavedUsageInflation,
|
|
@@ -3965,7 +3981,10 @@ async function repairCodebuddyLogJsonlOverlap({
|
|
|
3965
3981
|
let rebuiltRows = [];
|
|
3966
3982
|
try {
|
|
3967
3983
|
await parseCodebuddyIncremental({
|
|
3968
|
-
|
|
3984
|
+
// Rebuild from both sources. The parser now preserves legacy-only log
|
|
3985
|
+
// sessions while consuming mirrored log fingerprints against JSONL, so
|
|
3986
|
+
// rebuilding JSONL alone would silently discard valid pre-rawUsage data.
|
|
3987
|
+
projectFiles: files,
|
|
3969
3988
|
cursors: tmpCursors,
|
|
3970
3989
|
queuePath: tmpQueue,
|
|
3971
3990
|
env,
|
|
@@ -4053,6 +4072,172 @@ async function repairCodebuddyLogJsonlOverlap({
|
|
|
4053
4072
|
return true;
|
|
4054
4073
|
}
|
|
4055
4074
|
|
|
4075
|
+
// WorkBuddy's 0.87.9 SQLite fallback treated the bounded session_usage.used
|
|
4076
|
+
// context snapshot as cumulative input tokens. A current database proves that
|
|
4077
|
+
// assumption false (`size` is a 192k context limit while trace input totals can
|
|
4078
|
+
// exceed it), so rebuild historical WorkBuddy buckets from JSONL/trace sources
|
|
4079
|
+
// once the new parser is available. The same missing-source guard used by the
|
|
4080
|
+
// other data migrations keeps this recoverable: if a previously contributing
|
|
4081
|
+
// file is gone, leave history untouched and stop adding new context snapshots.
|
|
4082
|
+
async function repairWorkbuddyContextUsage({
|
|
4083
|
+
cursors,
|
|
4084
|
+
queuePath,
|
|
4085
|
+
queueStatePath,
|
|
4086
|
+
workbuddyFiles,
|
|
4087
|
+
env = process.env,
|
|
4088
|
+
} = {}) {
|
|
4089
|
+
if (!cursors || typeof cursors !== "object") return false;
|
|
4090
|
+
const migrations = (cursors.migrations ||= {});
|
|
4091
|
+
const prior = migrations[WORKBUDDY_CONTEXT_USAGE_REPAIR_KEY];
|
|
4092
|
+
if (prior && !(typeof prior === "object" && prior.skipped)) return false;
|
|
4093
|
+
|
|
4094
|
+
const hourly = cursors.hourly && typeof cursors.hourly === "object" ? cursors.hourly : {};
|
|
4095
|
+
const liveHourlyKeys = new Set(
|
|
4096
|
+
Object.keys(hourly.buckets || {}).filter((key) => key.startsWith("workbuddy|")),
|
|
4097
|
+
);
|
|
4098
|
+
let queueRaw = "";
|
|
4099
|
+
try {
|
|
4100
|
+
queueRaw = await fs.readFile(queuePath, "utf8");
|
|
4101
|
+
} catch (e) {
|
|
4102
|
+
if (e?.code !== "ENOENT") throw e;
|
|
4103
|
+
}
|
|
4104
|
+
const queueWorkbuddyKeys = new Set();
|
|
4105
|
+
const keptQueueLines = [];
|
|
4106
|
+
for (const line of queueRaw.split("\n")) {
|
|
4107
|
+
if (!line.trim()) continue;
|
|
4108
|
+
let row;
|
|
4109
|
+
try { row = JSON.parse(line); } catch (_e) {
|
|
4110
|
+
keptQueueLines.push(line);
|
|
4111
|
+
continue;
|
|
4112
|
+
}
|
|
4113
|
+
if (row && row.source === "workbuddy" && typeof row.model === "string" && typeof row.hour_start === "string") {
|
|
4114
|
+
queueWorkbuddyKeys.add(bucketKey("workbuddy", row.model || "unknown", row.hour_start));
|
|
4115
|
+
} else {
|
|
4116
|
+
keptQueueLines.push(line);
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
const liveKeys = new Set([...liveHourlyKeys, ...queueWorkbuddyKeys]);
|
|
4120
|
+
if (liveKeys.size === 0) {
|
|
4121
|
+
migrations[WORKBUDDY_CONTEXT_USAGE_REPAIR_KEY] = {
|
|
4122
|
+
status: "noop",
|
|
4123
|
+
appliedAt: new Date().toISOString(),
|
|
4124
|
+
reason: "no-workbuddy-history",
|
|
4125
|
+
};
|
|
4126
|
+
return false;
|
|
4127
|
+
}
|
|
4128
|
+
|
|
4129
|
+
const files = Array.isArray(workbuddyFiles) ? workbuddyFiles : [];
|
|
4130
|
+
const discoveredPaths = new Set(
|
|
4131
|
+
files.map((entry) => typeof entry === "string" ? entry : entry?.path).filter(Boolean),
|
|
4132
|
+
);
|
|
4133
|
+
const priorFiles = cursors.workbuddy?.fileOffsets && typeof cursors.workbuddy.fileOffsets === "object"
|
|
4134
|
+
? Object.keys(cursors.workbuddy.fileOffsets)
|
|
4135
|
+
: [];
|
|
4136
|
+
if (files.length === 0 || priorFiles.some((filePath) => !discoveredPaths.has(filePath))) {
|
|
4137
|
+
migrations[WORKBUDDY_CONTEXT_USAGE_REPAIR_KEY] = {
|
|
4138
|
+
skipped: true,
|
|
4139
|
+
reason: files.length === 0 ? "no-rebuild-sources" : "workbuddy_file_unreproducible",
|
|
4140
|
+
at: new Date().toISOString(),
|
|
4141
|
+
};
|
|
4142
|
+
return false;
|
|
4143
|
+
}
|
|
4144
|
+
|
|
4145
|
+
const tmpQueue = `${queuePath}.workbuddyrebuild.${process.pid}.${Date.now()}`;
|
|
4146
|
+
const tmpCursors = {
|
|
4147
|
+
version: 1,
|
|
4148
|
+
hourly: { buckets: {}, groupQueued: {} },
|
|
4149
|
+
workbuddy: {},
|
|
4150
|
+
};
|
|
4151
|
+
let rebuiltRows = [];
|
|
4152
|
+
try {
|
|
4153
|
+
await parseWorkbuddyIncremental({
|
|
4154
|
+
projectFiles: files,
|
|
4155
|
+
cursors: tmpCursors,
|
|
4156
|
+
queuePath: tmpQueue,
|
|
4157
|
+
env,
|
|
4158
|
+
});
|
|
4159
|
+
let rebuiltRaw = "";
|
|
4160
|
+
try { rebuiltRaw = await fs.readFile(tmpQueue, "utf8"); } catch (e) {
|
|
4161
|
+
if (e?.code !== "ENOENT") throw e;
|
|
4162
|
+
}
|
|
4163
|
+
rebuiltRows = rebuiltRaw.split("\n").filter((line) => line.trim());
|
|
4164
|
+
} catch (e) {
|
|
4165
|
+
console.error("[sync] WorkBuddy context-usage repair: rebuild failed, leaving history untouched:", e?.message || e);
|
|
4166
|
+
return false;
|
|
4167
|
+
} finally {
|
|
4168
|
+
await fs.rm(tmpQueue, { force: true }).catch(() => {});
|
|
4169
|
+
}
|
|
4170
|
+
|
|
4171
|
+
const rebuiltKeys = new Set();
|
|
4172
|
+
const validRebuiltRows = [];
|
|
4173
|
+
for (const line of rebuiltRows) {
|
|
4174
|
+
let row;
|
|
4175
|
+
try { row = JSON.parse(line); } catch (_e) { continue; }
|
|
4176
|
+
if (!row || row.source !== "workbuddy" || typeof row.model !== "string" || typeof row.hour_start !== "string") continue;
|
|
4177
|
+
rebuiltKeys.add(bucketKey("workbuddy", row.model || "unknown", row.hour_start));
|
|
4178
|
+
validRebuiltRows.push(line);
|
|
4179
|
+
}
|
|
4180
|
+
|
|
4181
|
+
const staleRetractions = [];
|
|
4182
|
+
for (const key of liveKeys) {
|
|
4183
|
+
if (rebuiltKeys.has(key)) continue;
|
|
4184
|
+
const [, model, ...hourParts] = key.split("|");
|
|
4185
|
+
staleRetractions.push(JSON.stringify({
|
|
4186
|
+
source: "workbuddy",
|
|
4187
|
+
model: model || "unknown",
|
|
4188
|
+
hour_start: hourParts.join("|"),
|
|
4189
|
+
input_tokens: 0,
|
|
4190
|
+
cached_input_tokens: 0,
|
|
4191
|
+
cache_creation_input_tokens: 0,
|
|
4192
|
+
output_tokens: 0,
|
|
4193
|
+
reasoning_output_tokens: 0,
|
|
4194
|
+
total_tokens: 0,
|
|
4195
|
+
billable_total_tokens: 0,
|
|
4196
|
+
conversation_count: 0,
|
|
4197
|
+
}));
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4200
|
+
const nextQueueLines = keptQueueLines.concat(staleRetractions, validRebuiltRows);
|
|
4201
|
+
await ensureDir(path.dirname(queuePath));
|
|
4202
|
+
const queueTmp = `${queuePath}.tmp.${process.pid}.${Date.now()}`;
|
|
4203
|
+
await fs.writeFile(queueTmp, nextQueueLines.length ? `${nextQueueLines.join("\n")}\n` : "", "utf8");
|
|
4204
|
+
await fs.rename(queueTmp, queuePath);
|
|
4205
|
+
|
|
4206
|
+
const nextHourly = (cursors.hourly ||= { buckets: {}, groupQueued: {} });
|
|
4207
|
+
nextHourly.buckets ||= {};
|
|
4208
|
+
nextHourly.groupQueued ||= {};
|
|
4209
|
+
for (const key of Object.keys(nextHourly.buckets)) {
|
|
4210
|
+
if (key.startsWith("workbuddy|")) delete nextHourly.buckets[key];
|
|
4211
|
+
}
|
|
4212
|
+
for (const key of Object.keys(nextHourly.groupQueued)) {
|
|
4213
|
+
if (key.startsWith("workbuddy|")) delete nextHourly.groupQueued[key];
|
|
4214
|
+
}
|
|
4215
|
+
for (const [key, bucket] of Object.entries(tmpCursors.hourly.buckets || {})) {
|
|
4216
|
+
if (key.startsWith("workbuddy|")) nextHourly.buckets[key] = bucket;
|
|
4217
|
+
}
|
|
4218
|
+
for (const [key, value] of Object.entries(tmpCursors.hourly.groupQueued || {})) {
|
|
4219
|
+
if (key.startsWith("workbuddy|")) nextHourly.groupQueued[key] = value;
|
|
4220
|
+
}
|
|
4221
|
+
cursors.workbuddy = tmpCursors.workbuddy || {};
|
|
4222
|
+
|
|
4223
|
+
let uploadState = {};
|
|
4224
|
+
try { uploadState = JSON.parse(await fs.readFile(queueStatePath, "utf8")); } catch (_e) {}
|
|
4225
|
+
uploadState.offset = 0;
|
|
4226
|
+
uploadState.updatedAt = new Date().toISOString();
|
|
4227
|
+
uploadState.note = "reset_after_workbuddy_context_usage_repair_2026_08";
|
|
4228
|
+
await ensureDir(path.dirname(queueStatePath));
|
|
4229
|
+
await fs.writeFile(queueStatePath, JSON.stringify(uploadState, null, 2) + "\n", "utf8");
|
|
4230
|
+
|
|
4231
|
+
migrations[WORKBUDDY_CONTEXT_USAGE_REPAIR_KEY] = {
|
|
4232
|
+
status: "applied",
|
|
4233
|
+
appliedAt: new Date().toISOString(),
|
|
4234
|
+
previousBuckets: liveKeys.size,
|
|
4235
|
+
rebuiltBuckets: rebuiltKeys.size,
|
|
4236
|
+
retractedBuckets: staleRetractions.length,
|
|
4237
|
+
};
|
|
4238
|
+
return true;
|
|
4239
|
+
}
|
|
4240
|
+
|
|
4056
4241
|
// One-time repair (#187): rebuild codex hourly buckets that the inode-keyed
|
|
4057
4242
|
// re-scan double-counted before the codexHashes event-dedup landed, and push
|
|
4058
4243
|
// the corrected values to the cloud. Runs BEFORE the codex parse in the same
|