tokentracker-cli 1.5.2 → 1.5.4
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-B5kmt0VD.js → AchievementBadge-CKNb_cwg.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-C0FlkOo0.js → AchievementsPage-u9P8DXJI.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-CRcp80Pp.js → AchievementsSection-BSCUJ4DR.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-DAFjGbKW.js → BadgeDetailModal-Bg7cE3Zq.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-CO0bbimB.js → BotAnimated-C7D2WbQA.js} +1 -1
- package/dashboard/dist/assets/{Card-Xhr5exz8.js → Card-B4IqAhai.js} +1 -1
- package/dashboard/dist/assets/{CloudCheckoutPage-DsQ7FmsI.js → CloudCheckoutPage-P75pH9Ym.js} +1 -1
- package/dashboard/dist/assets/{CloudPage-DkSexu5V.js → CloudPage-DDEZfmZg.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-Bm0huQdj.js → CommandPalette-CER_AklA.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-BsL8GN8Q.js → CommunityStatsModal-Cbb74eib.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-_WPp292g.js → ConfirmModal-Q1ZQdZHt.js} +1 -1
- package/dashboard/dist/assets/{Controls-ktzn68KM.js → Controls-DaGOfcoz.js} +1 -1
- package/dashboard/dist/assets/DashboardPage-CXI7RmxO.js +1 -0
- package/dashboard/dist/assets/{DateRangePopover-DWr4zIzF.js → DateRangePopover-DSS1Ira_.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-CBa8Mk47.js → DevicePage-6zPLYydh.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-CZ5jzgtb.js → DialogClose-DdCQ36LK.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-DztHQvj-.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-Dr0N6q15.js → DialogTitle-BjP217Y1.js} +1 -1
- package/dashboard/dist/assets/{ElectricLogo-sYqgrO6-.js → ElectricLogo-Dhq8PM4h.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-Qq-hcrZW.js → FadeIn-CGNbED4E.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-B77zSv7i.js → HeaderGithubStar-DwqEFzb6.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-lRDaUmoW.js → HoverTooltip-CQZTgyQy.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-B6AvHZjA.js → IpCheckPage-Bz09HXLn.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-DgzOjf3x.js → LandingPage-DC-lZsYR.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardPage-BtjjtDMC.js → LeaderboardPage-D8MpLM_t.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProBadge-B2wNTUTU.js → LeaderboardProBadge-pdXgokhE.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfileModal-DJJckwHh.js → LeaderboardProfileModal-nHZLiE0m.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-DJdxByl2.js → LeaderboardProfilePage-BKv3oIAW.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-TmRTgSCb.js +6 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-B2lVqf_H.js → LocalOnlyNotice-aF01udyR.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-i0gg1yby.js → LoginCard-BaroD0Ct.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-B-loLNZM.js → LoginModal-0jgyMGVM.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-D_swI75j.js → LoginPage-BUx8U58i.js} +1 -1
- package/dashboard/dist/assets/{PetPage-DDpKvpjG.js → PetPage-CpOjtJqg.js} +2 -2
- package/dashboard/dist/assets/{PopoverPopup-Gja4MyEK.js → PopoverPopup-BYjfer-M.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-DOPj7tAZ.js → ProviderIcon-NtYa2h0u.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-BVADLk89.js → ResetPasswordPage-CYUg7TR1.js} +1 -1
- package/dashboard/dist/assets/{Select-D-38tN-E.js → Select-CYP95o5y.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-jN61ukxk.js → SelectItemText-BCDfusOM.js} +1 -1
- package/dashboard/dist/assets/{SelfHostPage-k7AkGpl1.js → SelfHostPage-BmVc2rIT.js} +1 -1
- package/dashboard/dist/assets/ServiceStatusPage-Dyp8NwIn.js +1 -0
- package/dashboard/dist/assets/SessionsPage-tGEj6Ztz.js +3 -0
- package/dashboard/dist/assets/{SettingsPage-DfnMCAuA.js → SettingsPage-CCMbq5wc.js} +3 -3
- package/dashboard/dist/assets/{SkillsPage-YyIHIaHN.js → SkillsPage-C4O4fbaQ.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-BLcvZuQ9.js → TokenGalaxy-D2d738xr.js} +1 -1
- package/dashboard/dist/assets/{TooltipPopup-3E8kyVzz.js → TooltipPopup-BJfFYesG.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-ByKAdXSF.js → TrendMonitor-CElakFSU.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-BgEVIIzL.js → WidgetsPage-Cvd1LbJm.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-HjbX8QaA.js → WrappedPage-wRC1tE_a.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-Br-KAXh6.css → __vite-browser-external-CMBWGkOq.css} +1 -1
- package/dashboard/dist/assets/__vite-browser-external-V4TPLCJG.js +45 -0
- package/dashboard/dist/assets/{agent-logos-BVF2siPQ.js → agent-logos-Ba57HF3w.js} +1 -1
- package/dashboard/dist/assets/{arrow-left-CYO24PGP.js → arrow-left-B1bKnxXL.js} +1 -1
- package/dashboard/dist/assets/{arrow-right-Xg9PZYhw.js → arrow-right-DrOlKzX5.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-B7ah6Qv-.js → arrow-up-right-hlYpo3yw.js} +1 -1
- package/dashboard/dist/assets/{calendar-days-BV_iG938.js → calendar-days-CSY6SEsI.js} +1 -1
- package/dashboard/dist/assets/{check--EZH8RTp.js → check-xrP1gLw5.js} +1 -1
- package/dashboard/dist/assets/{chevron-down-BzGTlk0-.js → chevron-down-6-zT5n9d.js} +1 -1
- package/dashboard/dist/assets/{cloud-checkout-BgyxPHEh.js → cloud-checkout-BZymZD4K.js} +1 -1
- package/dashboard/dist/assets/{composite-C0KrmLEK.js → composite-C9izWafa.js} +1 -1
- package/dashboard/dist/assets/{copy-BtucSK2U.js → copy-Dkas5wZX.js} +1 -1
- package/dashboard/dist/assets/{download-CWHq03Y2.js → download-CagGRs0_.js} +1 -1
- package/dashboard/dist/assets/{external-link-DCTXF5VZ.js → external-link-DRl10hxd.js} +1 -1
- package/dashboard/dist/assets/{flame-BE5TsqVF.js → flame-CkvmsTFB.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-ThNIMWnZ.js → format-tokens-BtwhO3mV.js} +1 -1
- package/dashboard/dist/assets/{gift-DgaN9s4-.js → gift-YzjXkMQx.js} +1 -1
- package/dashboard/dist/assets/{icons-B4r5EiDC.js → icons-CHhKYs0u.js} +1 -1
- package/dashboard/dist/assets/{index-DqCtgU0G.js → index-B0MQZTrM.js} +1 -1
- package/dashboard/dist/assets/{index-fNKH4U7j.js → index-C2DK8av-.js} +1 -1
- package/dashboard/dist/assets/{index-CKhcCLNC.js → index-CJknN9Jd.js} +1 -1
- package/dashboard/dist/assets/{index-DFF9xo6f.js → index-DojzoMqd.js} +1 -1
- package/dashboard/dist/assets/{info-B4YzRR4y.js → info-Torep-Ti.js} +1 -1
- package/dashboard/dist/assets/{link-2-DzbUbaBw.js → link-2-DE5Yu-z6.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-B9c7D-7v.js → loader-circle-BL8LxD9n.js} +1 -1
- package/dashboard/dist/assets/{local-usage-transfer-btuk_b0Y.js → local-usage-transfer-8X1InWix.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-BjrU-Xz7.js → modifiers.esm-D60-6UQa.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-Bwn4tb4q.js → monitor-smartphone-C-IFPjqk.js} +1 -1
- package/dashboard/dist/assets/{play-DIFOwGIQ.js → play-n6uWrdH9.js} +1 -1
- package/dashboard/dist/assets/{plus-BpBqnwVg.js → plus-B2f6TaNj.js} +1 -1
- package/dashboard/dist/assets/{provider-display-DSrs4u7T.js → provider-display-DWqCEcsP.js} +1 -1
- package/dashboard/dist/assets/{react-BgG7_MY0.js → react-B-zfi_j6.js} +1 -1
- package/dashboard/dist/assets/{safePolygon-BUGWIDOM.js → safePolygon-C2PsvI-v.js} +1 -1
- package/dashboard/dist/assets/{search-aHrmBmtZ.js → search-DZOaTLF9.js} +1 -1
- package/dashboard/dist/assets/{server-DOEvwlEU.js → server-DIp4Qfsv.js} +1 -1
- package/dashboard/dist/assets/{shield-check-BPTnLhsT.js → shield-check-Cs2M8-iT.js} +1 -1
- package/dashboard/dist/assets/{skills-api-C052Mwez.js → skills-api-CoYy6Tov.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-DaZKhjhu.js → sortable.esm-B5_hjIfs.js} +1 -1
- package/dashboard/dist/assets/{store-Cq30Pofw.js → store-BLXqtJzb.js} +1 -1
- package/dashboard/dist/assets/{terminal-CoSDSkE7.js → terminal-CEMKX3ba.js} +1 -1
- package/dashboard/dist/assets/{trash-BAmt-kPj.js → trash-DOWYaXjK.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-Bxq0kBhm.js → use-community-stats-BjRI1Bzz.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-DmIQDlvk.js → use-latest-request-guard-T7L5qBU6.js} +1 -1
- package/dashboard/dist/assets/use-limits-display-prefs-vNIrCPZA.js +1 -0
- package/dashboard/dist/assets/{use-native-settings-s_CIC8Bb.js → use-native-settings-Cg5Zb4q_.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-BHhykAbt.js → use-ordered-list-BF-wIvmG.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-B9K5mIx4.js +1 -0
- package/dashboard/dist/assets/{use-transform-CVeMFlS6.js → use-transform-CbJ1IcNc.js} +1 -1
- package/dashboard/dist/assets/use-usage-limits-Dgg3jpC4.js +1 -0
- package/dashboard/dist/assets/{useCurrency-DilqL2Iv.js → useCurrency-3oBHWfSe.js} +1 -1
- package/dashboard/dist/assets/{usePositioner-BcnySw8a.js → usePositioner-D0YrIW0A.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-CvKW46WP.js → useTokenFormat-DZAHWE_R.js} +1 -1
- package/dashboard/dist/assets/{zap-D7VRNTwk.js → zap-kLub6Jiv.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/ip-check.html +2 -2
- package/dashboard/dist/leaderboard.html +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +1 -1
- package/src/commands/serve.js +25 -15
- package/src/lib/accept-encoding.js +59 -0
- package/src/lib/cursor-config.js +3 -1
- package/src/lib/deepseek-limits.js +280 -0
- package/src/lib/deepseek-status.js +157 -0
- package/src/lib/grok-limits.js +40 -1
- package/src/lib/local-api.js +248 -10
- package/src/lib/opencode-status.js +88 -0
- package/src/lib/pricing/index.js +182 -79
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/provider-api-key-store.js +2 -0
- package/src/lib/provider-status.js +27 -2
- package/src/lib/qoder-limits.js +17 -2
- package/src/lib/session-analytics.js +194 -29
- package/src/lib/static-server.js +74 -2
- package/src/lib/usage-limits.js +714 -53
- package/src/lib/workbuddy-limits.js +291 -26
- package/src/lib/zcode-limits.js +143 -35
- package/dashboard/dist/assets/DashboardPage-CdNXi3Im.js +0 -1
- package/dashboard/dist/assets/DialogDescription-kCZ4gN09.js +0 -1
- package/dashboard/dist/assets/LimitsPage-gSBHSBi_.js +0 -6
- package/dashboard/dist/assets/ServiceStatusPage-D_Fa_pYz.js +0 -1
- package/dashboard/dist/assets/SessionsPage-BymccEEz.js +0 -3
- package/dashboard/dist/assets/__vite-browser-external-D_cMeuqI.js +0 -45
- package/dashboard/dist/assets/use-limits-display-prefs-CyAn1fme.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-BmFyFE3S.js +0 -1
- package/dashboard/dist/assets/use-usage-limits-DRZMI20u.js +0 -1
package/src/lib/zcode-limits.js
CHANGED
|
@@ -1156,6 +1156,10 @@ function readZcodeLimitsCache({ home, nowMs = Date.now(), providerKeys = [], ide
|
|
|
1156
1156
|
configured: true,
|
|
1157
1157
|
error: null,
|
|
1158
1158
|
provider_key: providerKey,
|
|
1159
|
+
// Live results carry their provider's region for the other_region
|
|
1160
|
+
// attach; the cached shape mirrors them so a cache-served card keeps
|
|
1161
|
+
// that section too (issue #959).
|
|
1162
|
+
region: zcodeRegionFromProviderId(providerKey),
|
|
1159
1163
|
plan_kind: typeof raw.plan_kind === "string" ? raw.plan_kind : null,
|
|
1160
1164
|
plan_id: typeof raw.plan_id === "string" ? raw.plan_id : null,
|
|
1161
1165
|
plan_label: typeof raw.plan_label === "string" ? raw.plan_label : null,
|
|
@@ -1172,27 +1176,23 @@ function readZcodeLimitsCache({ home, nowMs = Date.now(), providerKeys = [], ide
|
|
|
1172
1176
|
return null;
|
|
1173
1177
|
}
|
|
1174
1178
|
|
|
1175
|
-
/**
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
+
/**
|
|
1180
|
+
* Merge partitions into the disk cache atomically; best-effort, never throws.
|
|
1181
|
+
* Cross-process semantics are last-writer-wins on purpose, matching the house
|
|
1182
|
+
* pattern (ark / qoder / codex caches): a concurrent writer can discard a
|
|
1183
|
+
* partition added between its read and rename, which costs one refresh cycle
|
|
1184
|
+
* — the next fetch rewrites it. Snapshots in particular keep getting
|
|
1185
|
+
* re-persisted on every fetch while their region's rows are inside the scan
|
|
1186
|
+
* window, so a lost write self-heals long before the window expires.
|
|
1187
|
+
*/
|
|
1188
|
+
function mergeZcodeLimitsCacheEntries(entries, { home } = {}) {
|
|
1179
1189
|
const cachePath = zcodeLimitsCachePath({ home });
|
|
1180
1190
|
let payload = {};
|
|
1181
1191
|
try {
|
|
1182
1192
|
const parsed = JSON.parse(fs.readFileSync(cachePath, "utf8"));
|
|
1183
1193
|
if (parsed && typeof parsed === "object") payload = parsed;
|
|
1184
1194
|
} catch (_error) {}
|
|
1185
|
-
payload
|
|
1186
|
-
identity,
|
|
1187
|
-
plan_kind: typeof limits.plan_kind === "string" ? limits.plan_kind : null,
|
|
1188
|
-
plan_id: typeof limits.plan_id === "string" ? limits.plan_id : null,
|
|
1189
|
-
plan_label: typeof limits.plan_label === "string" ? limits.plan_label : null,
|
|
1190
|
-
buckets: Array.isArray(limits.buckets) ? limits.buckets : [],
|
|
1191
|
-
primary_window: limits.primary_window || null,
|
|
1192
|
-
secondary_window: limits.secondary_window || null,
|
|
1193
|
-
tertiary_window: limits.tertiary_window || null,
|
|
1194
|
-
cached_at: new Date(nowMs).toISOString(),
|
|
1195
|
-
};
|
|
1195
|
+
Object.assign(payload, entries);
|
|
1196
1196
|
try {
|
|
1197
1197
|
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
1198
1198
|
const tmpPath = `${cachePath}.${process.pid}.tmp`;
|
|
@@ -1201,28 +1201,129 @@ function writeZcodeLimitsCache(limits, { home, nowMs = Date.now(), providerKey,
|
|
|
1201
1201
|
} catch (_error) {}
|
|
1202
1202
|
}
|
|
1203
1203
|
|
|
1204
|
+
/** Merge one provider partition into the disk cache; best-effort, never throws. */
|
|
1205
|
+
function writeZcodeLimitsCache(limits, { home, nowMs = Date.now(), providerKey, identity } = {}) {
|
|
1206
|
+
if (!providerKey || !identity || !limits?.configured || limits.error) return;
|
|
1207
|
+
if (!limits.primary_window && !limits.secondary_window && !limits.tertiary_window) return;
|
|
1208
|
+
mergeZcodeLimitsCacheEntries({
|
|
1209
|
+
[providerKey]: {
|
|
1210
|
+
identity,
|
|
1211
|
+
plan_kind: typeof limits.plan_kind === "string" ? limits.plan_kind : null,
|
|
1212
|
+
plan_id: typeof limits.plan_id === "string" ? limits.plan_id : null,
|
|
1213
|
+
plan_label: typeof limits.plan_label === "string" ? limits.plan_label : null,
|
|
1214
|
+
buckets: Array.isArray(limits.buckets) ? limits.buckets : [],
|
|
1215
|
+
primary_window: limits.primary_window || null,
|
|
1216
|
+
secondary_window: limits.secondary_window || null,
|
|
1217
|
+
tertiary_window: limits.tertiary_window || null,
|
|
1218
|
+
cached_at: new Date(nowMs).toISOString(),
|
|
1219
|
+
},
|
|
1220
|
+
}, { home });
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
// Issue #959: ZCode only queries the balance of the region currently in use,
|
|
1224
|
+
// so the other region's log rows stop appearing the moment it goes idle and
|
|
1225
|
+
// then age out of the 6h scan window — the other_region section would vanish.
|
|
1226
|
+
// While a region's rows are still fresh, snapshot its latest record into a
|
|
1227
|
+
// per-region cache partition; withZcodeOtherRegion falls back to it after the
|
|
1228
|
+
// window expires. The snapshot shape comes straight from
|
|
1229
|
+
// normalizeZcodeBalanceResponse, so the no-reset_credits / no-start_plan red
|
|
1230
|
+
// line holds structurally. Log records carry no login id, so unlike the
|
|
1231
|
+
// windows partitions there is no identity guard here: staleness is
|
|
1232
|
+
// self-attested by the snapshot's log_timestamp (rendered as the section's
|
|
1233
|
+
// time row), and the section stays collapsed and informational.
|
|
1234
|
+
function zcodeLogSnapshotCacheKey(region) {
|
|
1235
|
+
return `log-snapshot:${region}`;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
function writeZcodeLogSnapshotCache(byRegion, { home, nowMs = Date.now() } = {}) {
|
|
1239
|
+
if (!(byRegion instanceof Map) || byRegion.size === 0) return;
|
|
1240
|
+
const entries = {};
|
|
1241
|
+
for (const [region, record] of byRegion) {
|
|
1242
|
+
if (region !== "bigmodel" && region !== "zai") continue;
|
|
1243
|
+
let normalized;
|
|
1244
|
+
try {
|
|
1245
|
+
normalized = normalizeZcodeBalanceResponse(record.body);
|
|
1246
|
+
} catch (_error) {
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
entries[zcodeLogSnapshotCacheKey(region)] = {
|
|
1250
|
+
source: "zcode-log",
|
|
1251
|
+
region,
|
|
1252
|
+
provider_key: record.providerKey,
|
|
1253
|
+
log_timestamp: record.log_timestamp,
|
|
1254
|
+
...normalized,
|
|
1255
|
+
cached_at: new Date(nowMs).toISOString(),
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
if (Object.keys(entries).length > 0) mergeZcodeLimitsCacheEntries(entries, { home });
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
/**
|
|
1262
|
+
* Read one region's persisted log snapshot, shaped exactly like the fresh
|
|
1263
|
+
* withZcodeOtherRegion attach (issue #938). Deliberately no per-window
|
|
1264
|
+
* expiry: clipping aged windows would make the cached section render
|
|
1265
|
+
* differently from the same record served fresh inside the scan window.
|
|
1266
|
+
*/
|
|
1267
|
+
function readZcodeLogSnapshotCache(region, { home, nowMs = Date.now() } = {}) {
|
|
1268
|
+
if (region !== "bigmodel" && region !== "zai") return null;
|
|
1269
|
+
let parsed;
|
|
1270
|
+
try {
|
|
1271
|
+
parsed = JSON.parse(fs.readFileSync(zcodeLimitsCachePath({ home }), "utf8"));
|
|
1272
|
+
} catch (_error) {
|
|
1273
|
+
return null;
|
|
1274
|
+
}
|
|
1275
|
+
const raw = parsed?.[zcodeLogSnapshotCacheKey(region)];
|
|
1276
|
+
if (!raw || typeof raw !== "object" || raw.source !== "zcode-log" || raw.region !== region) return null;
|
|
1277
|
+
if (!Number.isFinite(Date.parse(typeof raw.log_timestamp === "string" ? raw.log_timestamp : ""))) return null;
|
|
1278
|
+
const cachedAtMs = Date.parse(typeof raw.cached_at === "string" ? raw.cached_at : "");
|
|
1279
|
+
if (!Number.isFinite(cachedAtMs) || cachedAtMs > nowMs + 60_000) return null;
|
|
1280
|
+
return {
|
|
1281
|
+
source: "zcode-log",
|
|
1282
|
+
region,
|
|
1283
|
+
provider_key: typeof raw.provider_key === "string" ? raw.provider_key : null,
|
|
1284
|
+
log_timestamp: raw.log_timestamp,
|
|
1285
|
+
plan_kind: typeof raw.plan_kind === "string" ? raw.plan_kind : null,
|
|
1286
|
+
plan_id: typeof raw.plan_id === "string" ? raw.plan_id : null,
|
|
1287
|
+
plan_label: typeof raw.plan_label === "string" ? raw.plan_label : null,
|
|
1288
|
+
buckets: Array.isArray(raw.buckets) ? raw.buckets : [],
|
|
1289
|
+
primary_window: raw.primary_window || null,
|
|
1290
|
+
secondary_window: raw.secondary_window || null,
|
|
1291
|
+
tertiary_window: raw.tertiary_window || null,
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1204
1295
|
/**
|
|
1205
1296
|
* Attach the freshest logged balance snapshot from the user's other region
|
|
1206
1297
|
* (issue #938): ZCode's own usage logs carry both regions' billing data even
|
|
1207
|
-
* when only the active region's credentials are on disk.
|
|
1298
|
+
* when only the active region's credentials are on disk. Once those rows age
|
|
1299
|
+
* out of the 6h scan window (ZCode only queries the active region's balance,
|
|
1300
|
+
* issue #959), fall back to the snapshot persisted by
|
|
1301
|
+
* writeZcodeLogSnapshotCache while they were fresh. Either way the section is
|
|
1208
1302
|
* read-only and deliberately carries no reset_credits / start_plan extras —
|
|
1209
1303
|
* those are login-scoped and must never ride along across regions.
|
|
1210
1304
|
*/
|
|
1211
|
-
function withZcodeOtherRegion(result, byRegion) {
|
|
1305
|
+
function withZcodeOtherRegion(result, byRegion, { home, env = process.env, nowMs = Date.now() } = {}) {
|
|
1212
1306
|
if (!result || result.error || !result.region) return result;
|
|
1213
1307
|
const otherRegion = result.region === "bigmodel" ? "zai" : "bigmodel";
|
|
1214
1308
|
const record = byRegion instanceof Map ? byRegion.get(otherRegion) : null;
|
|
1215
|
-
if (
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1309
|
+
if (record) {
|
|
1310
|
+
return {
|
|
1311
|
+
...result,
|
|
1312
|
+
other_region: {
|
|
1313
|
+
source: "zcode-log",
|
|
1314
|
+
region: otherRegion,
|
|
1315
|
+
provider_key: record.providerKey,
|
|
1316
|
+
log_timestamp: record.log_timestamp,
|
|
1317
|
+
...normalizeZcodeBalanceResponse(record.body),
|
|
1318
|
+
},
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
// Saved snapshots are log-derived data, so the log-fallback kill switch
|
|
1322
|
+
// silences them along with the scan that feeds them.
|
|
1323
|
+
if (env?.TOKENTRACKER_ZCODE_DISABLE_LOG_FALLBACK === "1") return result;
|
|
1324
|
+
const snapshot = readZcodeLogSnapshotCache(otherRegion, { home, nowMs });
|
|
1325
|
+
if (!snapshot) return result;
|
|
1326
|
+
return { ...result, other_region: snapshot };
|
|
1226
1327
|
}
|
|
1227
1328
|
|
|
1228
1329
|
/**
|
|
@@ -1264,8 +1365,9 @@ function loadZcodeLoginScopedTokens(family, auth, { home, env } = {}) {
|
|
|
1264
1365
|
* logs, and finally the last-good disk cache. The result carries the winning
|
|
1265
1366
|
* provider's `region` ("bigmodel" | "zai") and, when ZCode's own logs hold one,
|
|
1266
1367
|
* an `other_region` snapshot of the second region's plan (issue #938) — read-only
|
|
1267
|
-
* log data, never extended with login-scoped extras
|
|
1268
|
-
*
|
|
1368
|
+
* log data, never extended with login-scoped extras, served from the logs while
|
|
1369
|
+
* fresh and from the disk cache once the scan window expires (issue #959).
|
|
1370
|
+
* Credentials and internal team routing identifiers are omitted from the result.
|
|
1269
1371
|
*/
|
|
1270
1372
|
async function fetchZcodeLimits({ home, env, fetchImpl = fetch, nowMs = Date.now(), providerTimeoutMs } = {}) {
|
|
1271
1373
|
const startedAt = Date.now();
|
|
@@ -1283,11 +1385,15 @@ async function fetchZcodeLimits({ home, env, fetchImpl = fetch, nowMs = Date.now
|
|
|
1283
1385
|
providerKeys: authCandidates.map((auth) => auth.providerKey),
|
|
1284
1386
|
nowMs,
|
|
1285
1387
|
});
|
|
1388
|
+
// Issue #959: capture each region's freshest log record while it is still
|
|
1389
|
+
// inside the scan window, so the other_region section survives the window
|
|
1390
|
+
// expiring (ZCode stops writing rows for a region the moment it goes idle).
|
|
1391
|
+
writeZcodeLogSnapshotCache(logScan.byRegion, { home, nowMs });
|
|
1286
1392
|
const requireProviderMatch = authCandidates.some((auth) => auth.planKind === "coding-plan");
|
|
1287
1393
|
const selectLogFallback = (errors) => {
|
|
1288
1394
|
const record = logScan.preferred
|
|
1289
1395
|
|| (requireProviderMatch && authCandidates.length > 0 ? null : logScan.fallback);
|
|
1290
|
-
return withZcodeOtherRegion(zcodeLogFallbackResult(record, errors), logScan.byRegion);
|
|
1396
|
+
return withZcodeOtherRegion(zcodeLogFallbackResult(record, errors), logScan.byRegion, { home, env, nowMs });
|
|
1291
1397
|
};
|
|
1292
1398
|
if (!authCandidates.length) {
|
|
1293
1399
|
return selectLogFallback([]) || { configured: false };
|
|
@@ -1403,13 +1509,13 @@ async function fetchZcodeLimits({ home, env, fetchImpl = fetch, nowMs = Date.now
|
|
|
1403
1509
|
}
|
|
1404
1510
|
}
|
|
1405
1511
|
}
|
|
1406
|
-
return withZcodeOtherRegion(result, logScan.byRegion);
|
|
1512
|
+
return withZcodeOtherRegion(result, logScan.byRegion, { home, env, nowMs });
|
|
1407
1513
|
} catch (error) {
|
|
1408
1514
|
errors.push(`${auth.providerKey}: ${error?.message || "Unknown error"}`);
|
|
1409
1515
|
partitionFailures.push({ providerKey: auth.providerKey, authError: error?.authError === true });
|
|
1410
1516
|
}
|
|
1411
1517
|
}
|
|
1412
|
-
if (emptySuccess) return withZcodeOtherRegion(emptySuccess, logScan.byRegion);
|
|
1518
|
+
if (emptySuccess) return withZcodeOtherRegion(emptySuccess, logScan.byRegion, { home, env, nowMs });
|
|
1413
1519
|
// Rank fallback partitions in live candidate priority order, and withhold
|
|
1414
1520
|
// any partition whose attempts all ended in auth errors: re-login is
|
|
1415
1521
|
// actionable, so neither a stale log record nor stale cache bars may mask
|
|
@@ -1428,7 +1534,7 @@ async function fetchZcodeLimits({ home, env, fetchImpl = fetch, nowMs = Date.now
|
|
|
1428
1534
|
// A log record for a provider with no live candidate is untouched by this
|
|
1429
1535
|
// gate: nothing was attempted for it, so no error is being masked.
|
|
1430
1536
|
if (logRecord && !authErrorOnlyProviders.has(logRecord.providerKey)) {
|
|
1431
|
-
return withZcodeOtherRegion(zcodeLogFallbackResult(logRecord, errors), logScan.byRegion);
|
|
1537
|
+
return withZcodeOtherRegion(zcodeLogFallbackResult(logRecord, errors), logScan.byRegion, { home, env, nowMs });
|
|
1432
1538
|
}
|
|
1433
1539
|
// Last stop before the error result: serve the disk cache in live candidate
|
|
1434
1540
|
// priority order, scoped to the login that produced the windows so an
|
|
@@ -1440,7 +1546,7 @@ async function fetchZcodeLimits({ home, env, fetchImpl = fetch, nowMs = Date.now
|
|
|
1440
1546
|
identityByKey.get(auth.providerKey).add(zcodeCacheIdentity(loginIdFor(zcodeProviderFamily(auth.providerKey)), auth.teamContext));
|
|
1441
1547
|
}
|
|
1442
1548
|
const cached = readZcodeLimitsCache({ home, nowMs, providerKeys: cacheEligible, identityByKey });
|
|
1443
|
-
if (cached) return withZcodeOtherRegion(cached, logScan.byRegion);
|
|
1549
|
+
if (cached) return withZcodeOtherRegion(cached, logScan.byRegion, { home, env, nowMs });
|
|
1444
1550
|
return {
|
|
1445
1551
|
configured: true,
|
|
1446
1552
|
error: errors[0] || "Unknown error",
|
|
@@ -1474,6 +1580,8 @@ module.exports = {
|
|
|
1474
1580
|
fetchZcodeStartPlanGrants,
|
|
1475
1581
|
readZcodeLimitsCache,
|
|
1476
1582
|
writeZcodeLimitsCache,
|
|
1583
|
+
writeZcodeLogSnapshotCache,
|
|
1584
|
+
readZcodeLogSnapshotCache,
|
|
1477
1585
|
zcodeCacheIdentity,
|
|
1478
1586
|
fetchZcodeLimits,
|
|
1479
1587
|
};
|