tokentracker-cli 0.93.3 → 0.93.5
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.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dashboard/dist/assets/{AchievementBadge-Bl3bWjZw.js → AchievementBadge-td60VwNO.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-B6FrDjg8.js → AchievementsPage-C3yKXgId.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-D5TJtcI5.js → AchievementsSection-XM39rQkD.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-BFlrt34z.js → BadgeDetailModal-2TgBBPmw.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-KONFZbQT.js → BotAnimated-CG6_x0d2.js} +1 -1
- package/dashboard/dist/assets/{Card-DDZHhXlQ.js → Card-BUnCudVB.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-DMw54QJ_.js → ClawdAnimated-CNRu6v7I.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-CxVv0uo_.js → CommandPalette-D5vVqYkW.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-CR2MuFo3.js → CommunityStatsModal-D0V4iTI6.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-B-jvLsxn.js → ConfirmModal-CzxTtnXo.js} +1 -1
- package/dashboard/dist/assets/{Controls-DXA3Zxx0.js → Controls-nR5gzX__.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-0jQPu59U.js → DashboardPage-CpHXkuZn.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-CMR7jT8-.js → DevicePage-Emw9iVMd.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-B1j73B1g.js → DialogClose-Bh-pvxsM.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-DJ-RHxIt.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-eIgptOU-.js → DialogTitle-BG3ngtew.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-B8aisUg4.js → FadeIn-DOEfAW5p.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-B7MEGODn.js → HeaderGithubStar-DslGxMu4.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-DfBzMfW7.js → HoverTooltip-O2OKCBc3.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-BBvSL0Zz.js → IpCheckPage-Dv6LbNU_.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-CcX03ndP.js → LandingPage-BBfteqDF.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-Dk4UJx-j.js → LeaderboardAvatar-Ct6QagwN.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-CcIp7sjv.js → LeaderboardPage-CSbIdSm7.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-DzXfmNhy.js → LeaderboardProfileModal-BuUWkmBN.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-18ijOFN-.js → LeaderboardProfilePage-fVnV0Lo-.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-ZeRD1J97.js → LimitsPage-BHkkA3xN.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-DQ_wPyho.js → LocalOnlyNotice-BLlrqreb.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-DX5-Bxhr.js → LoginCard-CKV1uACo.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-_Rt3qUyK.js → LoginModal-RCpQtFuG.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-CvYcDpeR.js → LoginPage-vMV2kpef.js} +1 -1
- package/dashboard/dist/assets/{PetPage-D0D4mRwd.js → PetPage-CTgwpnsA.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-Bz3yRvv6.js → PopoverPopup-Bddr9R6x.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-PauvxgNb.js → ProviderIcon-BeXu2voQ.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-BiC1Us4d.js → ResetPasswordPage-CilGTQWZ.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-bW8MsUDI.js → SegmentedControl-r5MP3Uyx.js} +1 -1
- package/dashboard/dist/assets/{Select-BSpIj6-K.js → Select-BBUIbPTj.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-taYIkVIw.js → SelectItemText-CbuN7Sui.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-GMkqBtrt.js → ServiceStatusPage-WgmQviZa.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-QydYL4yt.js → SessionsPage-BDkdXQhW.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-BFvCcos0.js → SettingsPage-CsF8cG7I.js} +2 -2
- package/dashboard/dist/assets/{SkillsPage-CKci6NM2.js → SkillsPage-CVGra4ar.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-DMof07_u.js → TokenGalaxy-B0kFIG0N.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-CAsiI0PJ.js → ToolbarRootContext-BlWkiH2k.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-Dj2Uxs9x.js → TrendMonitor-C18_ADhU.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-DOr-nMn1.js → WidgetsPage-Ck7L4vBj.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-BZUv1rX5.js → WrappedPage-Bm5HMj3a.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-WKiEWJ30.js → __vite-browser-external-BCyVFzdP.js} +15 -15
- package/dashboard/dist/assets/{agent-logos-1FaE1SqE.js → agent-logos-PZX7lWJo.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-Bs5oicPH.js → arrow-up-right-DPSDEZPh.js} +1 -1
- package/dashboard/dist/assets/{check-Be2PwUMS.js → check-GGrgivnu.js} +1 -1
- package/dashboard/dist/assets/{chevron-right-DH93vlVo.js → chevron-right-CmSOUizu.js} +1 -1
- package/dashboard/dist/assets/{copy-xOLhiaQa.js → copy-ChMyV-hy.js} +1 -1
- package/dashboard/dist/assets/{download-qUjPQH7V.js → download-DGUkE9Jc.js} +1 -1
- package/dashboard/dist/assets/{external-link-D0nLaYYu.js → external-link-B_W-UDpi.js} +1 -1
- package/dashboard/dist/assets/{flame-DFEEnwot.js → flame-C9ccP-R8.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-BPq1moPg.js → format-tokens-CkXJmbVP.js} +1 -1
- package/dashboard/dist/assets/{icons-DxM91e9c.js → icons-D37cRKGk.js} +1 -1
- package/dashboard/dist/assets/{index-CEl88_eC.js → index-C-_fimm3.js} +1 -1
- package/dashboard/dist/assets/{index-C8Sb5l3M.js → index-C9OzYqU0.js} +1 -1
- package/dashboard/dist/assets/{index-BEi52jGG.js → index-Dt6WIU71.js} +1 -1
- package/dashboard/dist/assets/{info-C43iVFmY.js → info-C3-0C2W0.js} +1 -1
- package/dashboard/dist/assets/{limits-providers-xNF55jgX.js → limits-providers-DB1Zr3WN.js} +1 -1
- package/dashboard/dist/assets/{link-2-CfarpF4W.js → link-2-DqNEbzji.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-CIKyaEoN.js → loader-circle-BYoKm-f5.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-DlTJ_PyX.js → maximize-2-lVQDTDqs.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-CXV6l89k.js → modifiers.esm-Bww85WOd.js} +1 -1
- package/dashboard/dist/assets/module-D2D5r9gC.js +3 -0
- package/dashboard/dist/assets/{monitor-smartphone-6dCO7HCP.js → monitor-smartphone--KubNwvP.js} +1 -1
- package/dashboard/dist/assets/{provider-display-DNEUispM.js → provider-display-COV_Ft4A.js} +1 -1
- package/dashboard/dist/assets/{react-us685QUG.js → react-lNnu0y7U.js} +1 -1
- package/dashboard/dist/assets/{search-NcbnjXYq.js → search-CpHNMxpD.js} +1 -1
- package/dashboard/dist/assets/{skills-api-CFSZ441M.js → skills-api-Cf2qRQ1h.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-DiFLdkJ7.js → sortable.esm-DWdlF_7j.js} +1 -1
- package/dashboard/dist/assets/{store-B-d1Nz34.js → store-DJQqZJtg.js} +1 -1
- package/dashboard/dist/assets/{terminal-D3L94ngT.js → terminal-Cban5byW.js} +1 -1
- package/dashboard/dist/assets/{trash-2-esrIc05L.js → trash-2-nelaUHSh.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-C4nPlCzJ.js → use-community-stats-CBupXLW7.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-iXm2ziaO.js → use-limits-display-prefs-B4y_kzcd.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-DU8WcVsn.js → use-ordered-list-DEV58tGc.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-CRf4pBTt.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-CpGicS2y.js → use-session-efficiency-pref-wSk1wV6R.js} +1 -1
- package/dashboard/dist/assets/{use-transform-WxJRtugr.js → use-transform-NKavwYrX.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-55w4KAqE.js → use-usage-limits-Blr1HBCj.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-C6HKlFA6.js → useCurrency-B2704dMx.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-CZ1Na38Y.js → useScrollLock-BuPQ8T9m.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-BR7kJQ6x.js → useTokenFormat-DPxAFuMj.js} +1 -1
- package/dashboard/dist/assets/{zap-BcwbN9j5.js → zap-BgtTdM4q.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/serve.js +115 -4
- package/src/commands/status.js +3 -3
- package/src/commands/sync.js +146 -3
- package/src/lib/opencode-go-limits.js +85 -30
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +372 -29
- package/src/lib/trae-cn-config.js +11 -2
- package/src/lib/usage-limits.js +119 -1
- package/dashboard/dist/assets/DialogDescription-ZS29ikF4.js +0 -1
- package/dashboard/dist/assets/module-CV1mYFsv.js +0 -3
- package/dashboard/dist/assets/use-reduced-motion-DDhy4yg_.js +0 -1
package/src/commands/status.js
CHANGED
|
@@ -654,9 +654,9 @@ async function cmdStatus(argv = []) {
|
|
|
654
654
|
// official API, so status surfaces the opt-in flag, the resolved storage
|
|
655
655
|
// path, and whether the auth blob decrypts — the three inputs a "why is my
|
|
656
656
|
// TRAE CN data missing" diagnosis needs.
|
|
657
|
-
// A resolvable default path does NOT mean the app is installed - the
|
|
658
|
-
// resolver always derives one. Match the sync path semantics
|
|
659
|
-
// installed iff the storage file actually exists on disk.
|
|
657
|
+
// A resolvable default path does NOT mean the app is installed - the
|
|
658
|
+
// macOS/Windows resolver always derives one. Match the sync path semantics
|
|
659
|
+
// exactly: installed iff the storage file actually exists on disk.
|
|
660
660
|
const traeCnStoragePath = resolveTraeCnStoragePath({ env: process.env, home });
|
|
661
661
|
const traeCnInstalled = Boolean(
|
|
662
662
|
traeCnStoragePath && fssync.existsSync(traeCnStoragePath),
|
package/src/commands/sync.js
CHANGED
|
@@ -28,6 +28,7 @@ const {
|
|
|
28
28
|
readOpencodeDbMessages,
|
|
29
29
|
readMimoDbMessages,
|
|
30
30
|
readZcodeDbMessages,
|
|
31
|
+
hasZcodeNativeUsageSchema,
|
|
31
32
|
resolveQoderDbPaths,
|
|
32
33
|
resolveQoderCnDbPaths,
|
|
33
34
|
readQoderDbMessages,
|
|
@@ -257,6 +258,7 @@ const CODEX_COLD_SCAN_AUDIT_MAX_SYNCS = 288;
|
|
|
257
258
|
// state so the next sync (providerID-filtered reader) rebuilds it correctly.
|
|
258
259
|
const MIMO_PROVIDER_REPAIR_KEY = "mimoClaudeMislabelRepair_2026_06";
|
|
259
260
|
const DSH_LEGACY_SOURCE_MIGRATION_KEY = "deepseekHarnessSourceMigration_2026_08";
|
|
261
|
+
const ZCODE_NATIVE_USAGE_REPAIR_KEY = "zcodeNativeUsageRepair_2026_08";
|
|
260
262
|
const AUTO_SYNC_SOURCE_ALIASES = new Map([
|
|
261
263
|
["code", "every-code"],
|
|
262
264
|
["deepseek", "dsh"],
|
|
@@ -468,6 +470,9 @@ async function cmdSync(argv, context = {}) {
|
|
|
468
470
|
const traeCnNowMs = context && typeof context === "object"
|
|
469
471
|
? context.traeCnNowMs
|
|
470
472
|
: undefined;
|
|
473
|
+
const cursorSyncDeps = context && typeof context.cursorSyncDeps === "object"
|
|
474
|
+
? context.cursorSyncDeps
|
|
475
|
+
: {};
|
|
471
476
|
const syncDiagnostics = diagnostics && typeof diagnostics === "object" ? diagnostics : null;
|
|
472
477
|
const home = os.homedir();
|
|
473
478
|
const { trackerDir } = await resolveTrackerPaths({ home });
|
|
@@ -1403,6 +1408,18 @@ async function cmdSync(argv, context = {}) {
|
|
|
1403
1408
|
if (zcodePaths.native || zcodePaths.wsl) {
|
|
1404
1409
|
if (progress?.enabled) progress.start(`Parsing ZCode ${renderBar(0)} | buckets 0`);
|
|
1405
1410
|
try {
|
|
1411
|
+
const nativeUsageAvailable = [zcodePaths.native, zcodePaths.wsl]
|
|
1412
|
+
.filter(Boolean)
|
|
1413
|
+
.some((dbPath) => hasZcodeNativeUsageSchema(dbPath));
|
|
1414
|
+
if (nativeUsageAvailable) {
|
|
1415
|
+
await repairZcodeNativeUsageMigration({
|
|
1416
|
+
cursors,
|
|
1417
|
+
queuePath,
|
|
1418
|
+
queueStatePath,
|
|
1419
|
+
projectQueuePath,
|
|
1420
|
+
projectQueueStatePath,
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1406
1423
|
zcodeResult = await multiInstallParse({
|
|
1407
1424
|
paths: zcodePaths, parserFn: parseOpencodeDbForInstall, providerName: "zcode",
|
|
1408
1425
|
cursors, getParams: (p) => ({ dbPath: p, readFn: readZcodeDbMessages, source: "zcode", cursorKey: "zcode" }),
|
|
@@ -1640,14 +1657,17 @@ async function cmdSync(argv, context = {}) {
|
|
|
1640
1657
|
}
|
|
1641
1658
|
|
|
1642
1659
|
let cursorResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
1643
|
-
|
|
1644
|
-
|
|
1660
|
+
const cursorInstalled = cursorSyncDeps.isInstalled || isCursorInstalled;
|
|
1661
|
+
const cursorAuthExtractor = cursorSyncDeps.extractAuth || extractCursorSessionToken;
|
|
1662
|
+
const cursorUsageFetcher = cursorSyncDeps.fetchUsageCsv || fetchCursorUsageCsv;
|
|
1663
|
+
if (sourceAllowed("cursor") && cursorInstalled({ home })) {
|
|
1664
|
+
const cursorAuth = cursorAuthExtractor({ home });
|
|
1645
1665
|
if (cursorAuth) {
|
|
1646
1666
|
try {
|
|
1647
1667
|
if (progress?.enabled) {
|
|
1648
1668
|
progress.start(`Fetching Cursor usage...`);
|
|
1649
1669
|
}
|
|
1650
|
-
const csvText = await
|
|
1670
|
+
const csvText = await cursorUsageFetcher({ cookie: cursorAuth.cookie });
|
|
1651
1671
|
const records = parseCursorCsv(csvText);
|
|
1652
1672
|
if (records.length > 0) {
|
|
1653
1673
|
if (progress?.enabled) {
|
|
@@ -3105,6 +3125,7 @@ module.exports = {
|
|
|
3105
3125
|
repairCodexInterleavedUsageInflation,
|
|
3106
3126
|
repairDroidDuplicateSessionInflation,
|
|
3107
3127
|
repairMimoClaudeMislabel,
|
|
3128
|
+
repairZcodeNativeUsageMigration,
|
|
3108
3129
|
reincludeClaudeMemObserverFiles,
|
|
3109
3130
|
repairGrokQueueFromSessionSnapshots,
|
|
3110
3131
|
applyCloudConversationsBackfill,
|
|
@@ -4083,6 +4104,128 @@ async function repairMimoClaudeMislabel({
|
|
|
4083
4104
|
return true;
|
|
4084
4105
|
}
|
|
4085
4106
|
|
|
4107
|
+
async function resetUploadOffsetForZcodeNativeRepair(queueStatePath) {
|
|
4108
|
+
if (typeof queueStatePath !== "string" || !queueStatePath) return false;
|
|
4109
|
+
let state = {};
|
|
4110
|
+
try {
|
|
4111
|
+
state = JSON.parse(await fs.readFile(queueStatePath, "utf8"));
|
|
4112
|
+
} catch (_error) {
|
|
4113
|
+
state = {};
|
|
4114
|
+
}
|
|
4115
|
+
state.offset = 0;
|
|
4116
|
+
state.updatedAt = new Date().toISOString();
|
|
4117
|
+
state.note = "reset_after_zcode_native_usage_repair_2026_08";
|
|
4118
|
+
await ensureDir(path.dirname(queueStatePath));
|
|
4119
|
+
await fs.writeFile(queueStatePath, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
4120
|
+
return true;
|
|
4121
|
+
}
|
|
4122
|
+
|
|
4123
|
+
async function dropZcodeQueueRows(filePath, { retainRetractions = false } = {}) {
|
|
4124
|
+
let raw;
|
|
4125
|
+
try {
|
|
4126
|
+
raw = await fs.readFile(filePath, "utf8");
|
|
4127
|
+
} catch (error) {
|
|
4128
|
+
if (error?.code === "ENOENT") return { removed: 0, retractions: 0 };
|
|
4129
|
+
throw error;
|
|
4130
|
+
}
|
|
4131
|
+
const kept = [];
|
|
4132
|
+
const retractions = new Map();
|
|
4133
|
+
let removed = 0;
|
|
4134
|
+
for (const line of raw.split("\n")) {
|
|
4135
|
+
if (!line.trim()) continue;
|
|
4136
|
+
let row;
|
|
4137
|
+
try {
|
|
4138
|
+
row = JSON.parse(line);
|
|
4139
|
+
} catch (_error) {
|
|
4140
|
+
kept.push(line);
|
|
4141
|
+
continue;
|
|
4142
|
+
}
|
|
4143
|
+
if (row?.source === "zcode") {
|
|
4144
|
+
removed += 1;
|
|
4145
|
+
const model = typeof row.model === "string" && row.model.trim()
|
|
4146
|
+
? row.model.trim()
|
|
4147
|
+
: "unknown";
|
|
4148
|
+
const hourStart = typeof row.hour_start === "string" ? row.hour_start : "";
|
|
4149
|
+
if (retainRetractions && hourStart) {
|
|
4150
|
+
retractions.set(`${model}|${hourStart}`, {
|
|
4151
|
+
source: "zcode",
|
|
4152
|
+
model,
|
|
4153
|
+
hour_start: hourStart,
|
|
4154
|
+
input_tokens: 0,
|
|
4155
|
+
cached_input_tokens: 0,
|
|
4156
|
+
cache_creation_input_tokens: 0,
|
|
4157
|
+
output_tokens: 0,
|
|
4158
|
+
reasoning_output_tokens: 0,
|
|
4159
|
+
total_tokens: 0,
|
|
4160
|
+
conversation_count: 0,
|
|
4161
|
+
});
|
|
4162
|
+
}
|
|
4163
|
+
continue;
|
|
4164
|
+
}
|
|
4165
|
+
kept.push(line);
|
|
4166
|
+
}
|
|
4167
|
+
if (removed === 0) return { removed: 0, retractions: 0 };
|
|
4168
|
+
await backupExistingFile(filePath);
|
|
4169
|
+
const tmp = `${filePath}.zcoderepair.${process.pid}.${Date.now()}`;
|
|
4170
|
+
const rewritten = [
|
|
4171
|
+
...kept,
|
|
4172
|
+
...[...retractions.values()].map((row) => JSON.stringify(row)),
|
|
4173
|
+
];
|
|
4174
|
+
await fs.writeFile(tmp, rewritten.length ? rewritten.join("\n") + "\n" : "", "utf8");
|
|
4175
|
+
await fs.rename(tmp, filePath);
|
|
4176
|
+
return { removed, retractions: retractions.size };
|
|
4177
|
+
}
|
|
4178
|
+
|
|
4179
|
+
async function repairZcodeNativeUsageMigration({
|
|
4180
|
+
cursors,
|
|
4181
|
+
queuePath,
|
|
4182
|
+
queueStatePath,
|
|
4183
|
+
projectQueuePath,
|
|
4184
|
+
projectQueueStatePath,
|
|
4185
|
+
} = {}) {
|
|
4186
|
+
if (!cursors || typeof cursors !== "object") return false;
|
|
4187
|
+
const migrations = (cursors.migrations ||= {});
|
|
4188
|
+
if (migrations[ZCODE_NATIVE_USAGE_REPAIR_KEY]) return false;
|
|
4189
|
+
|
|
4190
|
+
const mainRepair = await dropZcodeQueueRows(queuePath, { retainRetractions: true });
|
|
4191
|
+
const projectRepair = projectQueuePath
|
|
4192
|
+
? await dropZcodeQueueRows(projectQueuePath)
|
|
4193
|
+
: { removed: 0, retractions: 0 };
|
|
4194
|
+
|
|
4195
|
+
const hourly = cursors.hourly && typeof cursors.hourly === "object" ? cursors.hourly : null;
|
|
4196
|
+
if (hourly?.buckets) {
|
|
4197
|
+
for (const key of Object.keys(hourly.buckets)) {
|
|
4198
|
+
if (key.startsWith("zcode|")) delete hourly.buckets[key];
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
if (hourly?.groupQueued) {
|
|
4202
|
+
for (const key of Object.keys(hourly.groupQueued)) {
|
|
4203
|
+
if (key.startsWith("zcode|")) delete hourly.groupQueued[key];
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
const projectHourly = cursors.projectHourly && typeof cursors.projectHourly === "object"
|
|
4207
|
+
? cursors.projectHourly
|
|
4208
|
+
: null;
|
|
4209
|
+
if (projectHourly?.buckets) {
|
|
4210
|
+
for (const key of Object.keys(projectHourly.buckets)) {
|
|
4211
|
+
if (key.includes("|zcode|")) delete projectHourly.buckets[key];
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
delete cursors.zcode;
|
|
4215
|
+
|
|
4216
|
+
if (mainRepair.removed > 0) await resetUploadOffsetForZcodeNativeRepair(queueStatePath);
|
|
4217
|
+
if (projectRepair.removed > 0) {
|
|
4218
|
+
await resetUploadOffsetForZcodeNativeRepair(projectQueueStatePath);
|
|
4219
|
+
}
|
|
4220
|
+
migrations[ZCODE_NATIVE_USAGE_REPAIR_KEY] = {
|
|
4221
|
+
appliedAt: new Date().toISOString(),
|
|
4222
|
+
removedMain: mainRepair.removed,
|
|
4223
|
+
removedProject: projectRepair.removed,
|
|
4224
|
+
retractions: mainRepair.retractions,
|
|
4225
|
+
};
|
|
4226
|
+
return true;
|
|
4227
|
+
}
|
|
4228
|
+
|
|
4086
4229
|
async function repairGrokQueueFromSessionSnapshots({ cursors, queuePath, queueStatePath } = {}) {
|
|
4087
4230
|
if (!cursors || typeof cursors !== "object") return false;
|
|
4088
4231
|
const grokState = (cursors.grok ||= {});
|
|
@@ -266,6 +266,9 @@ async function resolveWorkspaceId(authCookie, fetchImpl, timeoutMs) {
|
|
|
266
266
|
headers: postHeaders,
|
|
267
267
|
body: "[]"
|
|
268
268
|
});
|
|
269
|
+
if (response.status === 401 || response.status === 403) {
|
|
270
|
+
throw new Error("Unauthorized or forbidden (401/403)");
|
|
271
|
+
}
|
|
269
272
|
text = await response.text();
|
|
270
273
|
ids = parseWorkspaceIds(text);
|
|
271
274
|
} catch (postErr) {
|
|
@@ -414,7 +417,18 @@ function buildLocalWindow({ used, limit, resetMs }) {
|
|
|
414
417
|
// so we sum in SQLite and return a single row rather than streaming every
|
|
415
418
|
// opencode-go turn into JS — keeps the limits poll cheap, cf. the spawnSync
|
|
416
419
|
// freeze lesson).
|
|
417
|
-
|
|
420
|
+
//
|
|
421
|
+
// Two schema generations share the same file (see src/lib/rollout.js):
|
|
422
|
+
// v1 keeps the `message` table with `$.role`/flat `$.providerID`.
|
|
423
|
+
// v2 (`opencode2`) moved messages to `session_message` (role is now the
|
|
424
|
+
// `type` column) and nests the provider under `$.model.providerID`.
|
|
425
|
+
function buildGoAggregateSql({ sessionStart, weekStart, weekEnd, monthStart, monthEnd }, variant = "v1") {
|
|
426
|
+
const isV2 = variant === "v2";
|
|
427
|
+
const table = isV2 ? "session_message" : "message";
|
|
428
|
+
const rolePredicate = isV2 ? "type = 'assistant'" : "json_extract(data,'$.role') = 'assistant'";
|
|
429
|
+
const providerPredicate = isV2
|
|
430
|
+
? "json_extract(data,'$.model.providerID') = 'opencode-go'"
|
|
431
|
+
: "json_extract(data,'$.providerID') = 'opencode-go'";
|
|
418
432
|
return (
|
|
419
433
|
"SELECT " +
|
|
420
434
|
`COALESCE(SUM(CASE WHEN createdMs >= ${sessionStart} THEN cost ELSE 0 END), 0) AS sessionCost, ` +
|
|
@@ -425,15 +439,41 @@ function buildGoAggregateSql({ sessionStart, weekStart, weekEnd, monthStart, mon
|
|
|
425
439
|
"FROM (" +
|
|
426
440
|
"SELECT CAST(COALESCE(json_extract(data,'$.time.created'), time_created) AS INTEGER) AS createdMs, " +
|
|
427
441
|
"CAST(json_extract(data,'$.cost') AS REAL) AS cost " +
|
|
428
|
-
|
|
442
|
+
`FROM ${table} ` +
|
|
429
443
|
"WHERE json_valid(data) " +
|
|
430
|
-
|
|
431
|
-
|
|
444
|
+
`AND ${providerPredicate} ` +
|
|
445
|
+
`AND ${rolePredicate} ` +
|
|
432
446
|
"AND json_type(data,'$.cost') IN ('integer','real')" +
|
|
433
447
|
")"
|
|
434
448
|
);
|
|
435
449
|
}
|
|
436
450
|
|
|
451
|
+
// Detect whether the database has any rows in session_message. This is the
|
|
452
|
+
// shared opencode2 probe (see src/lib/rollout.js): a pure v1 database has an
|
|
453
|
+
// empty session_message table, and blindly running the v2 aggregate against it
|
|
454
|
+
// is harmless here (no JOIN, no directory lookup) — but the probe result still
|
|
455
|
+
// drives variant ordering so v2 is preferred when data exists.
|
|
456
|
+
//
|
|
457
|
+
// A database carrying BOTH tables resolves to "v1 first" on purpose: unlike the
|
|
458
|
+
// message parser (which dedups rows by sessionID|messageID), this aggregate is
|
|
459
|
+
// a blind SUM with no key to dedup on, so unioning two generations could
|
|
460
|
+
// double-count cost if the tables overlap. Undercounting an opt-in estimate is
|
|
461
|
+
// the safe side of that trade.
|
|
462
|
+
async function detectOpencodeGoHasSessionMessages(dbPath, sqliteOptions = {}) {
|
|
463
|
+
let rows;
|
|
464
|
+
try {
|
|
465
|
+
rows = await readSqliteJsonRowsAsync(
|
|
466
|
+
dbPath,
|
|
467
|
+
`SELECT (SELECT 1 FROM session_message LIMIT 1) AS hasRows`,
|
|
468
|
+
{ label: "OpenCode Go", timeout: 5_000, ...sqliteOptions },
|
|
469
|
+
);
|
|
470
|
+
} catch (_e) {
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
if (!Array.isArray(rows) || rows.length === 0) return null;
|
|
474
|
+
return Boolean(rows[0]?.hasRows);
|
|
475
|
+
}
|
|
476
|
+
|
|
437
477
|
// Returns { source:'local', primary/secondary/tertiary_window } or null when no
|
|
438
478
|
// opencode.db / no opencode-go rows are found. The caller labels this result as
|
|
439
479
|
// an estimate and only exposes it when explicitly opted in.
|
|
@@ -445,32 +485,42 @@ async function collectOpencodeGoLocal({ home, env = process.env, nowMs = Date.no
|
|
|
445
485
|
const weekStart = weekStartMs(nowMs);
|
|
446
486
|
const weekEnd = weekStart + WEEK_MS;
|
|
447
487
|
const { startMs: monthStart, endMs: monthEnd } = monthBoundsMs(nowMs);
|
|
448
|
-
const
|
|
488
|
+
const windowArgs = { sessionStart, weekStart, weekEnd, monthStart, monthEnd };
|
|
449
489
|
|
|
450
490
|
let agg = null;
|
|
451
491
|
for (const dbPath of paths) {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
492
|
+
// Each database can be a different OpenCode generation. When sqlite_master
|
|
493
|
+
// is unreadable, fall back to v1-then-v2 so the legacy query runs first.
|
|
494
|
+
const hasRows = await detectOpencodeGoHasSessionMessages(dbPath, sqliteOptions);
|
|
495
|
+
const variants = hasRows === true ? ["v2", "v1"] : hasRows === false ? ["v1", "v2"] : ["v1", "v2"];
|
|
496
|
+
|
|
497
|
+
for (const variant of variants) {
|
|
498
|
+
const sql = buildGoAggregateSql(windowArgs, variant);
|
|
499
|
+
let rows;
|
|
500
|
+
try {
|
|
501
|
+
rows = await readSqliteJsonRowsAsync(dbPath, sql, {
|
|
502
|
+
label: "OpenCode Go",
|
|
503
|
+
timeout: 5_000,
|
|
504
|
+
...sqliteOptions,
|
|
505
|
+
throwOnReadFailure: true,
|
|
506
|
+
});
|
|
507
|
+
} catch (_e) {
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
const row = rows && rows[0];
|
|
511
|
+
if (!row) continue;
|
|
512
|
+
const rowCount = Number(row.rowCount) || 0;
|
|
513
|
+
if (rowCount <= 0) continue;
|
|
514
|
+
if (!agg) agg = { sessionCost: 0, weeklyCost: 0, monthlyCost: 0, sessionOldest: null, rowCount: 0 };
|
|
515
|
+
agg.sessionCost += Number(row.sessionCost) || 0;
|
|
516
|
+
agg.weeklyCost += Number(row.weeklyCost) || 0;
|
|
517
|
+
agg.monthlyCost += Number(row.monthlyCost) || 0;
|
|
518
|
+
agg.rowCount += rowCount;
|
|
519
|
+
const oldest = row.sessionOldest == null ? null : Number(row.sessionOldest);
|
|
520
|
+
if (oldest != null && Number.isFinite(oldest)) {
|
|
521
|
+
agg.sessionOldest = agg.sessionOldest == null ? oldest : Math.min(agg.sessionOldest, oldest);
|
|
522
|
+
}
|
|
523
|
+
break; // this database answered — don't re-run the other schema on it
|
|
474
524
|
}
|
|
475
525
|
}
|
|
476
526
|
if (!agg || agg.rowCount <= 0) return null;
|
|
@@ -602,7 +652,13 @@ async function scrapeOpencodeGoWeb({ cfg, fetchImpl, nowMs, timeoutMs }) {
|
|
|
602
652
|
try {
|
|
603
653
|
workspaceId = await resolveWorkspaceId(cfg.authCookie, fetchImpl, timeoutMs);
|
|
604
654
|
} catch (err) {
|
|
605
|
-
|
|
655
|
+
const msg = sanitizeMessage(err?.message || err);
|
|
656
|
+
const isAuth = /401|403|Unauthorized or forbidden/i.test(msg);
|
|
657
|
+
return {
|
|
658
|
+
configured: true,
|
|
659
|
+
error: `Failed to resolve Workspace ID: ${msg}`,
|
|
660
|
+
...(isAuth ? { auth_error: true } : {}),
|
|
661
|
+
};
|
|
606
662
|
}
|
|
607
663
|
if (!workspaceId) {
|
|
608
664
|
return { configured: true, error: "Could not auto-resolve OpenCode Workspace ID from cookie. Please set OPENCODE_GO_WORKSPACE_ID manually." };
|
|
@@ -696,8 +752,7 @@ async function fetchOpencodeGoLimits({
|
|
|
696
752
|
return { ...api, source: "api" };
|
|
697
753
|
}
|
|
698
754
|
if (api?.auth_error) {
|
|
699
|
-
|
|
700
|
-
return result;
|
|
755
|
+
return { ...api, source: "api" };
|
|
701
756
|
}
|
|
702
757
|
if (!cfg) return api || { configured: true, error: "OpenCode Go unavailable" };
|
|
703
758
|
}
|