tokentracker-cli 1.5.14 → 1.5.16
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-DqJWve1h.js → AchievementBadge-CnnJGct8.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-CnuBayj3.js → AchievementsPage-CFkUJVxy.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-WRU5TxAB.js → AchievementsSection-CoLQX04M.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-B5ikEQik.js → BadgeDetailModal-s8rObEHY.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-CaMG6j-6.js → BotAnimated-BmBnL-i8.js} +1 -1
- package/dashboard/dist/assets/{Card-BL3vxENk.js → Card-BIqie5mN.js} +1 -1
- package/dashboard/dist/assets/{CloudCheckoutPage-CsUb8CJ7.js → CloudCheckoutPage-B_c8Q58A.js} +1 -1
- package/dashboard/dist/assets/{CloudPage-5ZiPFzeC.js → CloudPage-CfW_pDf2.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-CYd9nXW-.js → CommandPalette-BRryGPVJ.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-BItox7Sz.js → CommunityStatsModal-Cyh1Q9Fw.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-BE1ABR4z.js → ConfirmModal-DWBYqpLJ.js} +1 -1
- package/dashboard/dist/assets/{Controls-BRqIsaHT.js → Controls-CPSiCzVW.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-Cm0YuBlx.js → DashboardPage-JcS1xwUm.js} +1 -1
- package/dashboard/dist/assets/{DateRangePopover-CRwEu66e.js → DateRangePopover-DjuS6888.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-DFUPsGb_.js → DevicePage-DZ3qiDbT.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-DALl4GmP.js → DialogClose-CfORRnoT.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-ChihKLnm.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-BSR8ozyn.js → DialogTitle-DZO6-sTI.js} +1 -1
- package/dashboard/dist/assets/{ElectricLogo-BtrycBUW.js → ElectricLogo-BzR7mRin.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-CfhgvLVd.js → FadeIn-BxHaUvz_.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-N8wqAdOf.js → HeaderGithubStar-KGzKAmuQ.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-CwdMYIzZ.js → HoverTooltip-C6pASVDN.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-DCRor9cb.js → IpCheckPage-BJ8TROZC.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-s9xvTHSp.js → LandingPage-B15GRFpI.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardPage-CUj3WLMo.js → LeaderboardPage-C3hJ2Gh3.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProBadge-N4NOKZWj.js → LeaderboardProBadge-CRfBTxpt.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfileModal-09HiUyxm.js → LeaderboardProfileModal-DTGmcjWF.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-Cw9_rodG.js → LeaderboardProfilePage-BD04D7ML.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-F8b6kZa0.js → LimitsPage-LsDCIUKd.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-D1eEzDEv.js → LocalOnlyNotice-B3XQKD8k.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-0sIovG_J.js → LoginCard-DF1XcskU.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-BT3oMVak.js → LoginModal-DuyVT0Tt.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-CDsXdaE5.js → LoginPage-kge4kJKa.js} +1 -1
- package/dashboard/dist/assets/{PetPage-BEnC1azp.js → PetPage-DuYtsKza.js} +2 -2
- package/dashboard/dist/assets/{PopoverPopup-Dwzp-hyf.js → PopoverPopup-BkNNS3VG.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-Cu_lcuuu.js → ProviderIcon-z2TKXPlS.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-DrpEscm1.js → ResetPasswordPage-CXM9E6d1.js} +1 -1
- package/dashboard/dist/assets/{Select-wIR4qvEI.js → Select-CeoAayzi.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-CNuJgYps.js → SelectItemText-CRxvAgUV.js} +1 -1
- package/dashboard/dist/assets/{SelfHostPage-CWNLygpL.js → SelfHostPage-Cavstn-d.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-BId9t16Q.js → ServiceStatusPage-CdIeM_DX.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-C_klx7d8.js → SessionsPage-DPihAS26.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-nC73JaZ4.js → SettingsPage-zxOsLeE5.js} +3 -3
- package/dashboard/dist/assets/{SkillsPage-B2agou8Y.js → SkillsPage-BRGonzsC.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-BNXUWRwa.js → TokenGalaxy-D9-huQJ_.js} +1 -1
- package/dashboard/dist/assets/{TooltipPopup--X2kExQ0.js → TooltipPopup-BS99Tf44.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-BELHsTnz.js → TrendMonitor-DlNT4ObY.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-DWp0SMXA.js → WidgetsPage-DTFnmTwq.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-DgwczKHe.js → WrappedPage-DwMdnusQ.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-BHphZRSC.js → __vite-browser-external-DR_a-OTS.js} +2 -2
- package/dashboard/dist/assets/{agent-logos-Cfx5WzU5.js → agent-logos-D6kTooyz.js} +1 -1
- package/dashboard/dist/assets/{arrow-left-D83SdANw.js → arrow-left-jpuJhYL4.js} +1 -1
- package/dashboard/dist/assets/{arrow-right-D7ye9bmJ.js → arrow-right-n1hLkwiK.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-CLmwEwwC.js → arrow-up-right-fZmEfh7R.js} +1 -1
- package/dashboard/dist/assets/{calendar-days-D1qLlvh4.js → calendar-days-B0LdECvD.js} +1 -1
- package/dashboard/dist/assets/{check-BHJvi4ey.js → check-CcnNd8bi.js} +1 -1
- package/dashboard/dist/assets/{chevron-down-6RBPKNpt.js → chevron-down-DhbHBjVC.js} +1 -1
- package/dashboard/dist/assets/{cloud-checkout-DgWcF-at.js → cloud-checkout-9XOoux-p.js} +1 -1
- package/dashboard/dist/assets/{composite-D-0pfyMY.js → composite-DIDAkYhy.js} +1 -1
- package/dashboard/dist/assets/{copy-B6mJSQsD.js → copy-BNDBunFG.js} +1 -1
- package/dashboard/dist/assets/{download-D31Nn-bI.js → download-wlqdqJmq.js} +1 -1
- package/dashboard/dist/assets/{external-link-C_lK9S3p.js → external-link-BvssZ_45.js} +1 -1
- package/dashboard/dist/assets/{flame-1ZxhHshT.js → flame-DP8IUT3E.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-DJk25SkD.js → format-tokens-DMsx_fWk.js} +1 -1
- package/dashboard/dist/assets/{gift-BUZzFh-1.js → gift-zF4D6O4a.js} +1 -1
- package/dashboard/dist/assets/{icons-BVPThCa4.js → icons-Batw5aOg.js} +1 -1
- package/dashboard/dist/assets/{index-B-mxrdIz.js → index-3qKpTOzz.js} +1 -1
- package/dashboard/dist/assets/{index-CjPsyTl7.js → index-Cqla2Zq5.js} +1 -1
- package/dashboard/dist/assets/{index-C0zFsOWh.js → index-Dc8PQ8ZR.js} +1 -1
- package/dashboard/dist/assets/{index-CzstoU6d.js → index-dCfimErX.js} +1 -1
- package/dashboard/dist/assets/{info-DmU-vlJ6.js → info-DcUyOPKG.js} +1 -1
- package/dashboard/dist/assets/{link-2-BlBM7uw7.js → link-2-CS-cWLmx.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-Dhrb_Ocy.js → loader-circle-DKc-gwgL.js} +1 -1
- package/dashboard/dist/assets/{local-usage-transfer-BudmMtkd.js → local-usage-transfer-bHATORmq.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-kmw1VM8W.js → modifiers.esm-B4QifJzx.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-DtjXNC_x.js → monitor-smartphone-D8vpN9Jv.js} +1 -1
- package/dashboard/dist/assets/{play-9w46lbjy.js → play-D70P7DVT.js} +1 -1
- package/dashboard/dist/assets/{plus-DAf_WGKT.js → plus-YomnSdqY.js} +1 -1
- package/dashboard/dist/assets/{provider-display-BZ0XlB2q.js → provider-display-VXu7Hzwq.js} +1 -1
- package/dashboard/dist/assets/{react-B01ZJuZY.js → react-CUO9Gs2G.js} +1 -1
- package/dashboard/dist/assets/{safePolygon-B6_x12Gb.js → safePolygon-CbVKM3Lv.js} +1 -1
- package/dashboard/dist/assets/{search-D3dW9GbV.js → search-BUi1hAmV.js} +1 -1
- package/dashboard/dist/assets/{server-C2XZBar7.js → server-B-agbJYI.js} +1 -1
- package/dashboard/dist/assets/{shield-check-4FX5vbS7.js → shield-check-yHx3fQDo.js} +1 -1
- package/dashboard/dist/assets/{skills-api-BvW_MUyg.js → skills-api-DojEO2dS.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-CAa_BJgh.js → sortable.esm-r4Lz4ugj.js} +1 -1
- package/dashboard/dist/assets/{store-2AX2MNkz.js → store-GzQbKyT-.js} +1 -1
- package/dashboard/dist/assets/{terminal-CON7n_ir.js → terminal-BLCZgZTm.js} +1 -1
- package/dashboard/dist/assets/{trash-C2f88jSi.js → trash-BFGtCBJS.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-CgQu43xV.js → use-community-stats-DGdLAa8L.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-lShM46-3.js → use-latest-request-guard-l39bM4s0.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-8o7pbEg0.js → use-limits-display-prefs-JcWgMNA0.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-dN8shG3M.js → use-native-settings-0sXFw6mq.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-beJy2IqQ.js → use-ordered-list-mRdyZ18r.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-D2g3iGbi.js +1 -0
- package/dashboard/dist/assets/{use-transform-cJpAQr2d.js → use-transform-CbJqMa_Y.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-CuusZA6b.js → use-usage-limits-qQKS4sHR.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-Cx9UsKJK.js → useCurrency-BJyZCVtd.js} +1 -1
- package/dashboard/dist/assets/{usePositioner-TEClK6mF.js → usePositioner-zsiJqVGU.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-BNwTLiac.js → useTokenFormat-DxEKSp5H.js} +1 -1
- package/dashboard/dist/assets/{zap-CFgbVEhC.js → zap-D40-8VPk.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/lib/ark-agent-plan-limits.js +26 -55
- package/src/lib/ark-coding-plan-limits.js +31 -62
- package/src/lib/cloud-account.js +29 -3
- package/src/lib/limits-cache.js +475 -0
- package/src/lib/local-api.js +92 -5
- package/src/lib/pricing/curated-overrides.json +32 -0
- package/src/lib/pricing/matcher.js +4 -1
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/qoder-limits.js +35 -69
- package/src/lib/rollout.js +43 -47
- package/src/lib/usage-limits.js +370 -1063
- package/dashboard/dist/assets/DialogDescription-CO0hykis.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-S0bR_G1B.js +0 -1
package/src/lib/qoder-limits.js
CHANGED
|
@@ -6,6 +6,7 @@ const net = require("node:net");
|
|
|
6
6
|
const os = require("node:os");
|
|
7
7
|
const path = require("node:path");
|
|
8
8
|
const { fetchQoderCnCliLimits } = require("./qoder-cn-cli-limits");
|
|
9
|
+
const { createLimitsCache } = require("./limits-cache");
|
|
9
10
|
|
|
10
11
|
const QODER_SITES = {
|
|
11
12
|
international: {
|
|
@@ -406,18 +407,40 @@ function writeQoderActivityCache(window, {
|
|
|
406
407
|
} catch (_error) {}
|
|
407
408
|
}
|
|
408
409
|
|
|
409
|
-
|
|
410
|
-
|
|
410
|
+
// The active region partitions the cache by filename (cn vs global); the
|
|
411
|
+
// record itself stays account-agnostic. A window without a parseable reset is
|
|
412
|
+
// kept only within the 12h unknown-reset TTL, and the global max-age only
|
|
413
|
+
// bounds snapshots with no future-dated window — a dated survivor outlives it.
|
|
414
|
+
// Provenance must describe the data's age, not the transport (#955): a fresh
|
|
415
|
+
// hit re-serves the winning source it was written with, an older hit honestly
|
|
416
|
+
// reports the disk cache.
|
|
417
|
+
const qoderLimitsCache = createLimitsCache({
|
|
418
|
+
file: ({ namespace }) => (namespace
|
|
411
419
|
? `qoder-${namespace}-usage-limits-cache.json`
|
|
412
|
-
: QODER_LIMITS_CACHE_FILE
|
|
413
|
-
|
|
414
|
-
|
|
420
|
+
: QODER_LIMITS_CACHE_FILE),
|
|
421
|
+
key: "qoder",
|
|
422
|
+
maxAgeMs: QODER_LIMITS_CACHE_MAX_AGE_MS,
|
|
423
|
+
maxAgeGate: "ifNoDatedSurvivor",
|
|
424
|
+
unknownResetTtlMs: QODER_LIMITS_CACHE_UNKNOWN_RESET_TTL_MS,
|
|
425
|
+
fields: {
|
|
426
|
+
plan_label: "string",
|
|
427
|
+
quota_exceeded: "boolTrue",
|
|
428
|
+
// Remember the winning source so a fresh re-serve keeps honest provenance
|
|
429
|
+
// instead of always claiming "disk-cache" (#955).
|
|
430
|
+
source: { write: (record) => (typeof record?.source === "string" ? record.source : null) },
|
|
431
|
+
},
|
|
432
|
+
windowKeys: ["primary_window", "secondary_window", "tertiary_window"],
|
|
433
|
+
// tertiary_window is written only when present and read back only when it
|
|
434
|
+
// survives — primary/secondary always appear (null when dead).
|
|
435
|
+
sparseWindowKeys: ["tertiary_window"],
|
|
436
|
+
stale: "byAge",
|
|
437
|
+
freshTtlMs: QODER_LIMITS_CACHE_FRESH_TTL_MS,
|
|
438
|
+
cachedFlag: true,
|
|
439
|
+
source: "provenance",
|
|
440
|
+
});
|
|
415
441
|
|
|
416
|
-
function
|
|
417
|
-
|
|
418
|
-
const resetAtMs = Date.parse(window.reset_at || "");
|
|
419
|
-
if (Number.isFinite(resetAtMs)) return resetAtMs > nowMs ? window : null;
|
|
420
|
-
return nowMs - cachedAtMs <= QODER_LIMITS_CACHE_UNKNOWN_RESET_TTL_MS ? window : null;
|
|
442
|
+
function qoderLimitsCachePath({ home = os.homedir(), namespace } = {}) {
|
|
443
|
+
return qoderLimitsCache.path({ home, namespace });
|
|
421
444
|
}
|
|
422
445
|
|
|
423
446
|
function readQoderLimitsCache({
|
|
@@ -425,44 +448,7 @@ function readQoderLimitsCache({
|
|
|
425
448
|
nowMs = Date.now(),
|
|
426
449
|
namespace,
|
|
427
450
|
} = {}) {
|
|
428
|
-
|
|
429
|
-
const raw = JSON.parse(fs.readFileSync(qoderLimitsCachePath({ home, namespace }), "utf8"))?.qoder;
|
|
430
|
-
const cachedAtMs = Date.parse(raw?.cached_at || "");
|
|
431
|
-
if (!Number.isFinite(cachedAtMs) || cachedAtMs > nowMs + 60_000) return null;
|
|
432
|
-
const primaryWindow = qoderCachedWindow(raw.primary_window, { cachedAtMs, nowMs });
|
|
433
|
-
const secondaryWindow = qoderCachedWindow(raw.secondary_window, { cachedAtMs, nowMs });
|
|
434
|
-
const tertiaryWindow = qoderCachedWindow(raw.tertiary_window, { cachedAtMs, nowMs });
|
|
435
|
-
if (!primaryWindow && !secondaryWindow && !tertiaryWindow) return null;
|
|
436
|
-
const hasUnexpiredDatedWindow = [primaryWindow, secondaryWindow, tertiaryWindow]
|
|
437
|
-
.some((window) => Number.isFinite(Date.parse(window?.reset_at || "")));
|
|
438
|
-
if (nowMs - cachedAtMs > QODER_LIMITS_CACHE_MAX_AGE_MS && !hasUnexpiredDatedWindow) {
|
|
439
|
-
return null;
|
|
440
|
-
}
|
|
441
|
-
// Provenance must describe the data's age, not the transport (#955): a
|
|
442
|
-
// successful refresh writes this cache and immediately floors later log
|
|
443
|
-
// scans, so the next refresh re-serves the same payload. Marking every
|
|
444
|
-
// hit stale/disk-cache made the Limits page permanently partial.
|
|
445
|
-
const ageMs = nowMs - cachedAtMs;
|
|
446
|
-
const fresh = ageMs <= QODER_LIMITS_CACHE_FRESH_TTL_MS;
|
|
447
|
-
const writtenSource = typeof raw.source === "string" && raw.source.trim()
|
|
448
|
-
? raw.source.trim()
|
|
449
|
-
: null;
|
|
450
|
-
return {
|
|
451
|
-
configured: true,
|
|
452
|
-
error: null,
|
|
453
|
-
plan_label: typeof raw.plan_label === "string" ? raw.plan_label : null,
|
|
454
|
-
quota_exceeded: raw.quota_exceeded === true,
|
|
455
|
-
primary_window: primaryWindow,
|
|
456
|
-
secondary_window: secondaryWindow,
|
|
457
|
-
...(tertiaryWindow ? { tertiary_window: tertiaryWindow } : {}),
|
|
458
|
-
cached: true,
|
|
459
|
-
stale: !fresh,
|
|
460
|
-
cached_at: raw.cached_at,
|
|
461
|
-
source: fresh && writtenSource ? writtenSource : "disk-cache",
|
|
462
|
-
};
|
|
463
|
-
} catch (_error) {
|
|
464
|
-
return null;
|
|
465
|
-
}
|
|
451
|
+
return qoderLimitsCache.read({ home, nowMs, namespace });
|
|
466
452
|
}
|
|
467
453
|
|
|
468
454
|
function writeQoderLimitsCache(limits, {
|
|
@@ -470,27 +456,7 @@ function writeQoderLimitsCache(limits, {
|
|
|
470
456
|
nowMs = Date.now(),
|
|
471
457
|
namespace,
|
|
472
458
|
} = {}) {
|
|
473
|
-
|
|
474
|
-
const cachePath = qoderLimitsCachePath({ home, namespace });
|
|
475
|
-
const payload = {
|
|
476
|
-
qoder: {
|
|
477
|
-
plan_label: limits.plan_label || null,
|
|
478
|
-
quota_exceeded: limits.quota_exceeded === true,
|
|
479
|
-
primary_window: limits.primary_window || null,
|
|
480
|
-
secondary_window: limits.secondary_window || null,
|
|
481
|
-
...(limits.tertiary_window ? { tertiary_window: limits.tertiary_window } : {}),
|
|
482
|
-
// Remember the winning source so a fresh re-serve keeps honest
|
|
483
|
-
// provenance instead of always claiming "disk-cache" (#955).
|
|
484
|
-
source: typeof limits.source === "string" ? limits.source : null,
|
|
485
|
-
cached_at: new Date(nowMs).toISOString(),
|
|
486
|
-
},
|
|
487
|
-
};
|
|
488
|
-
try {
|
|
489
|
-
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
490
|
-
const tmpPath = `${cachePath}.${process.pid}.tmp`;
|
|
491
|
-
fs.writeFileSync(tmpPath, JSON.stringify(payload, null, 2), { encoding: "utf8", mode: 0o600 });
|
|
492
|
-
fs.renameSync(tmpPath, cachePath);
|
|
493
|
-
} catch (_error) {}
|
|
459
|
+
qoderLimitsCache.write(limits, { home, nowMs, namespace });
|
|
494
460
|
}
|
|
495
461
|
|
|
496
462
|
function buildQoderActivityHeaders(authStatus, {
|
package/src/lib/rollout.js
CHANGED
|
@@ -11641,26 +11641,14 @@ async function parseWorkbuddyIncremental({
|
|
|
11641
11641
|
const startOffset = stat.size < prevSize || inodeChanged ? 0 : prevSize;
|
|
11642
11642
|
if (stat.size <= startOffset) continue;
|
|
11643
11643
|
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
stream = fssync.createReadStream(filePath, {
|
|
11647
|
-
encoding: "utf8",
|
|
11648
|
-
start: startOffset,
|
|
11649
|
-
});
|
|
11650
|
-
} catch { continue; }
|
|
11651
|
-
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
11652
|
-
|
|
11653
|
-
for await (const line of rl) {
|
|
11654
|
-
if (!line || !line.trim()) continue;
|
|
11655
|
-
let entry;
|
|
11656
|
-
try { entry = JSON.parse(line); } catch { continue; }
|
|
11657
|
-
if (!entry || typeof entry !== "object") continue;
|
|
11644
|
+
const committedEndOffset = await scanOmpLikeTranscript(filePath, startOffset, OMP_LIKE_MAX_RECORD_BYTES, (entry) => {
|
|
11645
|
+
if (!entry || typeof entry !== "object") return;
|
|
11658
11646
|
|
|
11659
11647
|
// Usage is carried on ANY record with providerData.rawUsage — assistant
|
|
11660
11648
|
// messages AND function_call records. Aggregate them all; dedup per id.
|
|
11661
11649
|
const provider = entry.providerData;
|
|
11662
11650
|
const rawUsage = provider && typeof provider === "object" ? provider.rawUsage : null;
|
|
11663
|
-
if (!rawUsage || typeof rawUsage !== "object")
|
|
11651
|
+
if (!rawUsage || typeof rawUsage !== "object") return;
|
|
11664
11652
|
|
|
11665
11653
|
const sessionId =
|
|
11666
11654
|
typeof entry.sessionId === "string" && entry.sessionId
|
|
@@ -11668,10 +11656,10 @@ async function parseWorkbuddyIncremental({
|
|
|
11668
11656
|
: path.basename(filePath, ".jsonl");
|
|
11669
11657
|
const sqliteSession = sqliteSessions[sessionId];
|
|
11670
11658
|
if (sqliteSession?.detailed || tracedSessionIds.has(sessionId)) {
|
|
11671
|
-
|
|
11659
|
+
return;
|
|
11672
11660
|
}
|
|
11673
11661
|
const tsMs =
|
|
11674
|
-
Number.isFinite(Number(entry.timestamp)) && Number(entry.timestamp) > 0
|
|
11662
|
+
Number.isFinite(new Date(Number(entry.timestamp)).getTime()) && Number(entry.timestamp) > 0
|
|
11675
11663
|
? Number(entry.timestamp)
|
|
11676
11664
|
: null;
|
|
11677
11665
|
// One usage record per LLM round-trip; providerData.messageId is the
|
|
@@ -11688,24 +11676,24 @@ async function parseWorkbuddyIncremental({
|
|
|
11688
11676
|
: tsMs != null
|
|
11689
11677
|
? `${sessionId}:${tsMs}`
|
|
11690
11678
|
: null;
|
|
11691
|
-
if (!messageId)
|
|
11692
|
-
if (seenIds.has(messageId))
|
|
11679
|
+
if (!messageId) return;
|
|
11680
|
+
if (seenIds.has(messageId)) return;
|
|
11693
11681
|
|
|
11694
11682
|
recordsProcessed++;
|
|
11695
11683
|
|
|
11696
11684
|
const delta = workbuddyUsageDelta(rawUsage);
|
|
11697
11685
|
if (delta.total_tokens === 0) {
|
|
11698
11686
|
seenIds.add(messageId);
|
|
11699
|
-
|
|
11687
|
+
return;
|
|
11700
11688
|
}
|
|
11701
11689
|
|
|
11702
11690
|
if (tsMs == null) {
|
|
11703
11691
|
seenIds.add(messageId);
|
|
11704
|
-
|
|
11692
|
+
return;
|
|
11705
11693
|
}
|
|
11706
11694
|
const tsIso = new Date(tsMs).toISOString();
|
|
11707
11695
|
const bucketStart = toUtcHalfHourStart(tsIso);
|
|
11708
|
-
if (!bucketStart)
|
|
11696
|
+
if (!bucketStart) return;
|
|
11709
11697
|
|
|
11710
11698
|
const model =
|
|
11711
11699
|
normalizeModelInput(provider.model) ||
|
|
@@ -11735,14 +11723,14 @@ async function parseWorkbuddyIncremental({
|
|
|
11735
11723
|
bucketsQueued: touchedBuckets.size,
|
|
11736
11724
|
});
|
|
11737
11725
|
}
|
|
11738
|
-
}
|
|
11726
|
+
}, { recoverReadErrors: true });
|
|
11739
11727
|
|
|
11740
|
-
|
|
11741
|
-
|
|
11728
|
+
// As in the project pass, acknowledge only records consumed in full. An
|
|
11729
|
+
// unfinished tail or an append after EOF must remain visible next sync.
|
|
11742
11730
|
fileOffsets[filePath] = {
|
|
11743
|
-
size:
|
|
11744
|
-
mtimeMs:
|
|
11745
|
-
ino:
|
|
11731
|
+
size: committedEndOffset,
|
|
11732
|
+
mtimeMs: stat.mtimeMs,
|
|
11733
|
+
ino: stat.ino,
|
|
11746
11734
|
};
|
|
11747
11735
|
}
|
|
11748
11736
|
|
|
@@ -11850,7 +11838,7 @@ async function parseWorkbuddyIncremental({
|
|
|
11850
11838
|
if (sqliteSession?.detailed || tracedSessionIds.has(sessionId)) return;
|
|
11851
11839
|
|
|
11852
11840
|
const tsMs =
|
|
11853
|
-
Number.isFinite(Number(entry.timestamp)) && Number(entry.timestamp) > 0
|
|
11841
|
+
Number.isFinite(new Date(Number(entry.timestamp)).getTime()) && Number(entry.timestamp) > 0
|
|
11854
11842
|
? Number(entry.timestamp)
|
|
11855
11843
|
: null;
|
|
11856
11844
|
if (tsMs == null) return;
|
|
@@ -11890,6 +11878,7 @@ async function parseWorkbuddyIncremental({
|
|
|
11890
11878
|
addTotals(projectBucket.totals, delta);
|
|
11891
11879
|
projectTouchedBuckets.add(projectBucketKey(projectKey, source, bucketStart));
|
|
11892
11880
|
},
|
|
11881
|
+
{ recoverReadErrors: true },
|
|
11893
11882
|
);
|
|
11894
11883
|
|
|
11895
11884
|
// Commit only past records that were read in full: a record appended
|
|
@@ -16891,7 +16880,7 @@ function ompEntryTimestampMs(msg, entry) {
|
|
|
16891
16880
|
// records are orders of magnitude smaller.
|
|
16892
16881
|
const OMP_LIKE_MAX_RECORD_BYTES = 64 * 1024 * 1024;
|
|
16893
16882
|
|
|
16894
|
-
async function scanOmpLikeTranscript(filePath, startOffset, maxRecordBytes, onEntry) {
|
|
16883
|
+
async function scanOmpLikeTranscript(filePath, startOffset, maxRecordBytes, onEntry, { recoverReadErrors = false } = {}) {
|
|
16895
16884
|
let stream;
|
|
16896
16885
|
try {
|
|
16897
16886
|
stream = fssync.createReadStream(filePath, { start: startOffset });
|
|
@@ -16900,26 +16889,33 @@ async function scanOmpLikeTranscript(filePath, startOffset, maxRecordBytes, onEn
|
|
|
16900
16889
|
}
|
|
16901
16890
|
let scannedEndOffset = startOffset;
|
|
16902
16891
|
let committedEndOffset = startOffset;
|
|
16903
|
-
|
|
16904
|
-
|
|
16905
|
-
|
|
16906
|
-
|
|
16907
|
-
|
|
16908
|
-
|
|
16909
|
-
|
|
16910
|
-
|
|
16892
|
+
try {
|
|
16893
|
+
for await (const record of physicalJsonlRecords(stream, {
|
|
16894
|
+
invalidUtf8: "record",
|
|
16895
|
+
maxRecordBytes,
|
|
16896
|
+
})) {
|
|
16897
|
+
scannedEndOffset += record.physicalBytes;
|
|
16898
|
+
if (record.terminated) {
|
|
16899
|
+
committedEndOffset = scannedEndOffset;
|
|
16900
|
+
if (record.oversized || !record.utf8Valid || !record.line || !record.line.trim()) continue;
|
|
16901
|
+
let entry;
|
|
16902
|
+
try { entry = JSON.parse(record.line); } catch { continue; }
|
|
16903
|
+
onEntry(entry);
|
|
16904
|
+
continue;
|
|
16905
|
+
}
|
|
16906
|
+
// A final incomplete record may still be in the process of being written.
|
|
16907
|
+
if (record.oversized || !record.utf8Valid || !record.line || !record.line.trim()) break;
|
|
16911
16908
|
let entry;
|
|
16912
|
-
try { entry = JSON.parse(record.line); } catch {
|
|
16909
|
+
try { entry = JSON.parse(record.line); } catch { break; }
|
|
16913
16910
|
onEntry(entry);
|
|
16914
|
-
|
|
16911
|
+
committedEndOffset = scannedEndOffset;
|
|
16912
|
+
break;
|
|
16915
16913
|
}
|
|
16916
|
-
|
|
16917
|
-
|
|
16918
|
-
|
|
16919
|
-
|
|
16920
|
-
|
|
16921
|
-
committedEndOffset = scannedEndOffset;
|
|
16922
|
-
break;
|
|
16914
|
+
} catch (error) {
|
|
16915
|
+
// WorkBuddy can publish complete records and continue with other files or
|
|
16916
|
+
// fallbacks after a read failure. Unfinished bytes are never acknowledged.
|
|
16917
|
+
// Do not swallow callback/parser errors, or change other readers' policy.
|
|
16918
|
+
if (!recoverReadErrors || stream.errored !== error) throw error;
|
|
16923
16919
|
}
|
|
16924
16920
|
return committedEndOffset;
|
|
16925
16921
|
}
|