tokentracker-cli 1.5.3 → 1.5.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/dashboard/dist/assets/{AchievementBadge-uuDGO9Mq.js → AchievementBadge-BYxdWhS1.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-BCTVT7Jo.js → AchievementsPage-Bv9o7Aj8.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-B6fJSJSr.js → AchievementsSection-DE5bZGxg.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-FjUN__9G.js → BadgeDetailModal-CgZaSVTp.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-B-KqY_Wr.js → BotAnimated-CwhPfUBI.js} +1 -1
- package/dashboard/dist/assets/{Card-DS8T8piP.js → Card-Y7qaxMuE.js} +1 -1
- package/dashboard/dist/assets/{CloudCheckoutPage-_0UjsFPu.js → CloudCheckoutPage-DVl7jk6b.js} +1 -1
- package/dashboard/dist/assets/{CloudPage-BstgVBaV.js → CloudPage-BnIvpHGH.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-DtVsYZgc.js → CommandPalette-CzMv2Zxn.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-DTpEJqV3.js → CommunityStatsModal-D8sOZqUc.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-Lfdh2vr7.js → ConfirmModal-Bun5xKMm.js} +1 -1
- package/dashboard/dist/assets/{Controls-DpwQ7pkq.js → Controls-ltfnvkFW.js} +1 -1
- package/dashboard/dist/assets/DashboardPage-C3Xu6_3t.js +1 -0
- package/dashboard/dist/assets/{DateRangePopover-CrWgrzT2.js → DateRangePopover-e3gJYqFJ.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-DbK5KWJS.js → DevicePage-JgT16dEp.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-Dd2B_o03.js → DialogClose-DeWert7h.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-Ca6W3nCT.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-3JQBVha3.js → DialogTitle-BfK_yOeR.js} +1 -1
- package/dashboard/dist/assets/{ElectricLogo-LYAqG8pG.js → ElectricLogo-Bbev0Y8Q.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-DhDvUor7.js → FadeIn-fypYWLDK.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-9f1XmgYC.js → HeaderGithubStar-Cnil2Lbo.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-BXhHAkP7.js → HoverTooltip-Bw_8tru-.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-ChPdDeMl.js → IpCheckPage-CHnhUgA2.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-DnJ7Y9zw.js → LandingPage-Bq4iQS6S.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardPage-BF9isLzu.js → LeaderboardPage-D42lUdw-.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProBadge-BM4JKgkC.js → LeaderboardProBadge-eTKceT90.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfileModal-DfZABOpN.js → LeaderboardProfileModal-D-3tRrOR.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-BaZ38iT5.js → LeaderboardProfilePage-DkRCJoiA.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-dUXjP3_1.js +6 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-DvhlYoqQ.js → LocalOnlyNotice-CtpjB6Z2.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-g_Gbnzhs.js → LoginCard-zrRd8o54.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-DkJrI6b7.js → LoginModal-DNl5Rt1e.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-CqCKSo0n.js → LoginPage-BuB707vV.js} +1 -1
- package/dashboard/dist/assets/{PetPage-DbF7ROsj.js → PetPage-BpDriXkp.js} +2 -2
- package/dashboard/dist/assets/{PopoverPopup-4WuH41ZT.js → PopoverPopup-rQNSs8R6.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-6j0A2KD9.js → ProviderIcon-CumyO371.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-DBd-iwuM.js → ResetPasswordPage-ecUHfJAk.js} +1 -1
- package/dashboard/dist/assets/{Select-BBEpLeiG.js → Select-YVEqEhk4.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-Cn_U_rrO.js → SelectItemText-D1NBc4uc.js} +1 -1
- package/dashboard/dist/assets/{SelfHostPage-2WZc6Rml.js → SelfHostPage-8nAin68c.js} +1 -1
- package/dashboard/dist/assets/ServiceStatusPage-D1zxuzOg.js +1 -0
- package/dashboard/dist/assets/SessionsPage-B6ksEER_.js +3 -0
- package/dashboard/dist/assets/{SettingsPage-rXNr96i-.js → SettingsPage-COqP8ZbJ.js} +3 -3
- package/dashboard/dist/assets/{SkillsPage-CudTvbZL.js → SkillsPage-DCX7xTj0.js} +1 -1
- package/dashboard/dist/assets/TokenGalaxy-D4n7AR-R.js +4431 -0
- package/dashboard/dist/assets/{TooltipPopup-CsLKXhWC.js → TooltipPopup-DvSoB_eS.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-8onUTbkk.js → TrendMonitor-5GVzh0Ye.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-CcwE0lz8.js → WidgetsPage-BIc4MBiu.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-Cj9LLYqP.js → WrappedPage-BpQk4g0o.js} +1 -1
- package/dashboard/dist/assets/__vite-browser-external-B_rKFTTN.js +45 -0
- package/dashboard/dist/assets/__vite-browser-external-CMBWGkOq.css +1 -0
- package/dashboard/dist/assets/{agent-logos-ChBA1nAH.js → agent-logos-DVg-sTm4.js} +1 -1
- package/dashboard/dist/assets/{arrow-left-Bb5mfoPc.js → arrow-left-Ch6Q7eU5.js} +1 -1
- package/dashboard/dist/assets/{arrow-right-D1Ti2XUI.js → arrow-right-B2B9GvSI.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-B_CjA_y8.js → arrow-up-right-DwNO_uSb.js} +1 -1
- package/dashboard/dist/assets/{calendar-days-DdfrAqie.js → calendar-days---NjJlBi.js} +1 -1
- package/dashboard/dist/assets/{check-29zGw1MK.js → check-D3mhgRrW.js} +1 -1
- package/dashboard/dist/assets/{chevron-down-DAzlFX7g.js → chevron-down-x-b39Zto.js} +1 -1
- package/dashboard/dist/assets/{cloud-checkout-C2SeCjFM.js → cloud-checkout-CycWyTsg.js} +1 -1
- package/dashboard/dist/assets/{composite-DIf735Hq.js → composite-CnA1qtYn.js} +1 -1
- package/dashboard/dist/assets/{copy-BOuOM0HQ.js → copy-JRnRMgdX.js} +1 -1
- package/dashboard/dist/assets/{download-50tQRpGe.js → download-CRLqGD62.js} +1 -1
- package/dashboard/dist/assets/{external-link-BhlQClVe.js → external-link-BrmaWV_t.js} +1 -1
- package/dashboard/dist/assets/{flame-CeGfA-0L.js → flame-meQ5-vRD.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-Cq7YiAkJ.js → format-tokens-D8mIJGz1.js} +1 -1
- package/dashboard/dist/assets/{gift-Do-UFhiT.js → gift-Cd0FPXgO.js} +1 -1
- package/dashboard/dist/assets/{icons-C6EZ_HmP.js → icons-D-PTKw7b.js} +1 -1
- package/dashboard/dist/assets/{index-BWhbBAd9.js → index-CMV9UKNf.js} +1 -1
- package/dashboard/dist/assets/{index-c0crpe9Z.js → index-C_6gEM83.js} +1 -1
- package/dashboard/dist/assets/{index-BFrbVqsm.js → index-DCIZaLNC.js} +1 -1
- package/dashboard/dist/assets/index-HLYcwmPD.js +502 -0
- package/dashboard/dist/assets/{info-B4MXmsNE.js → info-e0tP9P3D.js} +1 -1
- package/dashboard/dist/assets/{link-2-DuWCslzl.js → link-2-DZ2TQhER.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-C2I3NZAO.js → loader-circle-DjflRntu.js} +1 -1
- package/dashboard/dist/assets/{local-usage-transfer-BQasvsUF.js → local-usage-transfer-XTZLA3XH.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-DbeNS6FT.js → modifiers.esm-BpEQ8-Gt.js} +1 -1
- package/dashboard/dist/assets/module-BbVsRaE2.js +3 -0
- package/dashboard/dist/assets/{monitor-smartphone-C6Y4ktsc.js → monitor-smartphone-VVBcI30G.js} +1 -1
- package/dashboard/dist/assets/{play-CwgcDJC2.js → play-DN4Sb3W_.js} +1 -1
- package/dashboard/dist/assets/{plus-B796wsUO.js → plus-Cz_k7arH.js} +1 -1
- package/dashboard/dist/assets/{provider-display-Cw26RFDm.js → provider-display-S1mSaydF.js} +1 -1
- package/dashboard/dist/assets/{react-d7Bk1-_K.js → react-MQBcjrn_.js} +1 -1
- package/dashboard/dist/assets/{safePolygon-CDE0dNv_.js → safePolygon-InKxYcXd.js} +1 -1
- package/dashboard/dist/assets/{search-DtKvsdRs.js → search-C-N0RDi0.js} +1 -1
- package/dashboard/dist/assets/{server-gAURHUyz.js → server-CZkn8vIZ.js} +1 -1
- package/dashboard/dist/assets/{shield-check-DFVdDTwh.js → shield-check-DPrGFouZ.js} +1 -1
- package/dashboard/dist/assets/{skills-api-2rdPUrJI.js → skills-api-wMwM-GpF.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-DFlfwnJr.js → sortable.esm-CUPP1ihT.js} +1 -1
- package/dashboard/dist/assets/{store-BKPCk_de.js → store-BpVdRYnQ.js} +1 -1
- package/dashboard/dist/assets/{terminal-BtWFakvS.js → terminal-C-7TFeKg.js} +1 -1
- package/dashboard/dist/assets/{trash-bpr9zTAM.js → trash-Be8cd4HD.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-DMnGgLne.js → use-community-stats-cG0WrB4Y.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-BSBlJJHe.js → use-latest-request-guard-C91enyPD.js} +1 -1
- package/dashboard/dist/assets/use-limits-display-prefs-DdwoLqPD.js +1 -0
- package/dashboard/dist/assets/{use-native-settings-OthqiF6g.js → use-native-settings-BR-NiMKh.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-DpAtr0R-.js → use-ordered-list-oqV724BP.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-DW5YHBBI.js +1 -0
- package/dashboard/dist/assets/{use-transform-BRz9dz_2.js → use-transform-BsNSgVD6.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-CwmhudAX.js → use-usage-limits-Dk8LcMU-.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-CAA-tQ27.js → useCurrency-FxerRfd2.js} +1 -1
- package/dashboard/dist/assets/{usePositioner-Cj2DPmwM.js → usePositioner-D2J-rSfX.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-Bf5-f7Hx.js → useTokenFormat-CaI2nl2C.js} +1 -1
- package/dashboard/dist/assets/{zap-B8Fg6PaC.js → zap-D3C37XAj.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 +4 -4
- package/src/commands/serve.js +3 -2
- package/src/lib/accept-encoding.js +59 -0
- package/src/lib/cursor-config.js +18 -2
- package/src/lib/deepseek-limits.js +280 -0
- package/src/lib/grok-limits.js +40 -1
- package/src/lib/local-api.js +229 -31
- package/src/lib/opencode-status.js +88 -0
- package/src/lib/pricing/curated-overrides.json +14 -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 +17 -5
- package/src/lib/rollout.js +13 -4
- package/src/lib/session-analytics.js +194 -29
- package/src/lib/static-server.js +74 -2
- package/src/lib/usage-limits.js +801 -26
- package/src/lib/workbuddy-limits.js +106 -14
- package/dashboard/dist/assets/DashboardPage-DUFWejkE.js +0 -1
- package/dashboard/dist/assets/DialogDescription-CLy0arbt.js +0 -1
- package/dashboard/dist/assets/LimitsPage-C7LbG3LX.js +0 -6
- package/dashboard/dist/assets/ServiceStatusPage-B-g4MDl0.js +0 -1
- package/dashboard/dist/assets/SessionsPage-CWS0iQ1L.js +0 -3
- package/dashboard/dist/assets/TokenGalaxy-BRKrbTsJ.js +0 -4426
- package/dashboard/dist/assets/__vite-browser-external-07djSGmy.js +0 -45
- package/dashboard/dist/assets/__vite-browser-external-BDldtsD2.css +0 -1
- package/dashboard/dist/assets/index-D9kwYWLX.js +0 -502
- package/dashboard/dist/assets/module-ZyVsxBG_.js +0 -3
- package/dashboard/dist/assets/use-limits-display-prefs-eYlvnUKn.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-DF0grwHs.js +0 -1
|
@@ -25,10 +25,10 @@ const { resolveWorkbuddyHome } = require("./rollout");
|
|
|
25
25
|
const { UNAVAILABLE, readWorkbuddyIpcDiscovery, openWorkbuddyIpc } = require("./workbuddy-ipc");
|
|
26
26
|
|
|
27
27
|
const WORKBUDDY_LIMITS_CACHE_FILE = "workbuddy-usage-limits-cache.json";
|
|
28
|
-
// Windows without a usable reset_at — the pooled resource window
|
|
29
|
-
// plus a plan row whose cycle end never
|
|
30
|
-
// quota rollover, so they are only
|
|
31
|
-
// to trust.
|
|
28
|
+
// Windows without a usable reset_at — the pooled resource window unless it
|
|
29
|
+
// carries a readable expiring_at, plus a plan row whose cycle end never
|
|
30
|
+
// parsed — cannot be checked against a quota rollover, so they are only
|
|
31
|
+
// served while the snapshot is young enough to trust.
|
|
32
32
|
const WORKBUDDY_LIMITS_CACHE_UNKNOWN_RESET_TTL_MS = 12 * 60 * 60 * 1000;
|
|
33
33
|
|
|
34
34
|
const PLAN_PRIORITY = Object.freeze({
|
|
@@ -195,6 +195,9 @@ function normalizePackageRow(row) {
|
|
|
195
195
|
used,
|
|
196
196
|
cycleEnd: firstString(row, ["CycleEndTime", "cycleEnd", "cycle_end"])
|
|
197
197
|
|| firstString(row, ["DeductionEndTime", "expireAt"]),
|
|
198
|
+
// Set once a grant has terminated (spent or revoked); active grants
|
|
199
|
+
// carry an empty string.
|
|
200
|
+
terminatedAt: firstString(row, ["ExpiredTime", "expiredTime"]),
|
|
198
201
|
};
|
|
199
202
|
}
|
|
200
203
|
|
|
@@ -406,6 +409,59 @@ function resourceWindow(pool) {
|
|
|
406
409
|
};
|
|
407
410
|
}
|
|
408
411
|
|
|
412
|
+
// Resource packages die individually: paid add-ons and reward grants each
|
|
413
|
+
// carry their own cycle end, so the pooled window can advertise when its
|
|
414
|
+
// credits start to lapse — "by <day>, <credits> expire" beats a bare date
|
|
415
|
+
// because a date alone cannot say how much is at stake. Grants are bucketed
|
|
416
|
+
// by local calendar day and only the earliest day that still holds credit is
|
|
417
|
+
// reported; later days belong in a per-grant breakdown, not the pooled bar.
|
|
418
|
+
// Rows that could be the subscription plan are not resource credit (the same
|
|
419
|
+
// rule collectResourceGroups applies), and a grant with no remaining credit,
|
|
420
|
+
// a past end, an unreadable end, or a termination stamp has nothing to lose.
|
|
421
|
+
function collectExpiringResourceCredits(rows, identity = {}, nowMs = Date.now()) {
|
|
422
|
+
const grants = new Map();
|
|
423
|
+
for (const row of rows) {
|
|
424
|
+
if (!row || isSelectablePlan(row, identity.code)) continue;
|
|
425
|
+
if (row.terminatedAt) continue;
|
|
426
|
+
const remain = row.remain
|
|
427
|
+
?? (row.total !== null && row.used !== null ? Math.max(0, row.total - row.used) : null);
|
|
428
|
+
if (remain === null || remain <= 0) continue;
|
|
429
|
+
const endMs = Date.parse(normalizeResetAt(row.cycleEnd) || "");
|
|
430
|
+
if (!Number.isFinite(endMs) || endMs <= nowMs) continue;
|
|
431
|
+
// The summary and the paid/free pages list the same grant; one code with
|
|
432
|
+
// one end time is one grant, and the later listing wins the way
|
|
433
|
+
// mergePackageRows merges.
|
|
434
|
+
grants.set(`${row.code}\u0000${endMs}`, { endMs, remain });
|
|
435
|
+
}
|
|
436
|
+
let earliest = null;
|
|
437
|
+
for (const grant of grants.values()) {
|
|
438
|
+
const day = localDayNumber(grant.endMs);
|
|
439
|
+
if (!earliest || day < earliest.day) {
|
|
440
|
+
earliest = { day, endMs: grant.endMs, firstMs: grant.endMs, credits: grant.remain };
|
|
441
|
+
} else if (day === earliest.day) {
|
|
442
|
+
// Grants lapsing the same day merge; the moment to advertise is the
|
|
443
|
+
// last one, when the day's final grant dies, while the first one marks
|
|
444
|
+
// when the pool's numbers actually start to drift.
|
|
445
|
+
earliest.endMs = Math.max(earliest.endMs, grant.endMs);
|
|
446
|
+
earliest.firstMs = Math.min(earliest.firstMs, grant.endMs);
|
|
447
|
+
earliest.credits += grant.remain;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (!earliest) return null;
|
|
451
|
+
return {
|
|
452
|
+
at: new Date(earliest.endMs).toISOString(),
|
|
453
|
+
firstAt: new Date(earliest.firstMs).toISOString(),
|
|
454
|
+
credits: earliest.credits,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Year*10000 + month*100 + day, local time — an ordering key that survives
|
|
459
|
+
// unpadded string comparison and matches how the end times parse.
|
|
460
|
+
function localDayNumber(ms) {
|
|
461
|
+
const date = new Date(ms);
|
|
462
|
+
return date.getFullYear() * 10000 + (date.getMonth() + 1) * 100 + date.getDate();
|
|
463
|
+
}
|
|
464
|
+
|
|
409
465
|
function englishPlanLabel(row, identity, variantId) {
|
|
410
466
|
// An actual English subscription/package name takes precedence over the
|
|
411
467
|
// client's edition fallback. Unknown localized tiers stay unnamed.
|
|
@@ -521,12 +577,22 @@ function workbuddyLimitsCachePath({ home } = {}) {
|
|
|
521
577
|
|
|
522
578
|
// A window whose reset_at has already passed is stale data, not a usable
|
|
523
579
|
// fallback — the quota has rolled over, so drop it. Undated windows (the
|
|
524
|
-
// resource pool, or a plan row without a
|
|
525
|
-
// checked against a reset and are bounded by
|
|
580
|
+
// resource pool without a readable expiring_at, or a plan row without a
|
|
581
|
+
// readable cycle end) cannot be checked against a reset and are bounded by
|
|
582
|
+
// the snapshot-age TTL instead.
|
|
526
583
|
function workbuddyCachedWindow(window, { cachedAtMs, nowMs } = {}) {
|
|
527
584
|
if (!window || typeof window !== "object") return null;
|
|
528
585
|
const resetAtMs = Date.parse(window.reset_at || "");
|
|
529
586
|
if (Number.isFinite(resetAtMs)) return resetAtMs > nowMs ? window : null;
|
|
587
|
+
// The resource pool has no reset but may carry its earliest grant expiry.
|
|
588
|
+
// expiring_first_at is the first moment the advertised credits start to
|
|
589
|
+
// lapse — from then on the pool's numbers no longer describe the account,
|
|
590
|
+
// so the snapshot is trusted exactly until then; expiring_at (the day's
|
|
591
|
+
// end) is only the display anchor. The same discipline as a reset date,
|
|
592
|
+
// not the undated TTL.
|
|
593
|
+
const expiringAtMs = Date.parse(window.expiring_first_at || "");
|
|
594
|
+
const anchorMs = Number.isFinite(expiringAtMs) ? expiringAtMs : Date.parse(window.expiring_at || "");
|
|
595
|
+
if (Number.isFinite(anchorMs)) return anchorMs > nowMs ? window : null;
|
|
530
596
|
return nowMs - cachedAtMs <= WORKBUDDY_LIMITS_CACHE_UNKNOWN_RESET_TTL_MS ? window : null;
|
|
531
597
|
}
|
|
532
598
|
|
|
@@ -656,18 +722,27 @@ async function postBilling(fetchImpl, url, token, body = {}, signal) {
|
|
|
656
722
|
return response.json();
|
|
657
723
|
}
|
|
658
724
|
|
|
659
|
-
async function fetchPlanQuota(post, variantId) {
|
|
725
|
+
async function fetchPlanQuota(post, variantId, nowMs) {
|
|
660
726
|
const prefix = "/billing/meter/";
|
|
661
727
|
const summary = await post(`${prefix}get-user-resource-summary`, {});
|
|
662
728
|
if (!unwrapBillingBody(summary)) throw new Error("WorkBuddy quota request was rejected.");
|
|
663
729
|
const identity = readSubscriptionIdentity(summary);
|
|
730
|
+
const summaryRows = collectPackageRows(summary);
|
|
664
731
|
const planCodes = [...new Set([...Object.keys(PLAN_PRIORITY),
|
|
665
732
|
...(identity.code && !isRewardCode(identity.code) ? [identity.code] : [])])];
|
|
733
|
+
// Resource grants ride along in the same pages query. The summary carries
|
|
734
|
+
// their aggregated pool row without a cycle end, so the per-grant rows —
|
|
735
|
+
// where each grant's own CycleEndTime lives — only come back when their
|
|
736
|
+
// package codes are requested; a plan-code-only listing never sees them.
|
|
737
|
+
const resourceCodes = summaryRows
|
|
738
|
+
.filter((row) => !isSelectablePlan(row, identity.code))
|
|
739
|
+
.map((row) => row.code);
|
|
740
|
+
const packageCodes = [...new Set([...planCodes, ...resourceCodes])];
|
|
666
741
|
const packageRows = await Promise.all(["paid", "free"].map(async (kind) => {
|
|
667
742
|
const rows = [];
|
|
668
743
|
for (let page = 1; page <= 10; page++) {
|
|
669
744
|
const payload = await post(`${prefix}get-user-resource-${kind}-packages`, {
|
|
670
|
-
PageNumber: page, PageSize: 100, PackageCodes:
|
|
745
|
+
PageNumber: page, PageSize: 100, PackageCodes: packageCodes,
|
|
671
746
|
Status: [0, 3], ...(kind === "paid" ? { NeedRenewInfo: true } : {}),
|
|
672
747
|
});
|
|
673
748
|
const data = unwrapBillingBody(payload);
|
|
@@ -678,14 +753,28 @@ async function fetchPlanQuota(post, variantId) {
|
|
|
678
753
|
}
|
|
679
754
|
throw new Error("WorkBuddy quota package list was incomplete.");
|
|
680
755
|
}));
|
|
681
|
-
const quota = selectWorkbuddyPlanQuota([
|
|
682
|
-
|
|
683
|
-
|
|
756
|
+
const quota = selectWorkbuddyPlanQuota([...summaryRows, ...packageRows.flat()], identity, variantId);
|
|
757
|
+
const secondary = resourceWindow(collectResourceGroups(summary));
|
|
758
|
+
if (secondary) {
|
|
759
|
+
// The pooled bar cannot say which credits lapse first; the grant rows
|
|
760
|
+
// carry each grant's cycle end. The summary's own pool row has no cycle
|
|
761
|
+
// end and skips the expiry bucket, so grants are not double-counted. A
|
|
762
|
+
// pool without a window is dropped wholesale, so expiry has nothing to
|
|
763
|
+
// attach to.
|
|
764
|
+
const expiring = collectExpiringResourceCredits(
|
|
765
|
+
[...summaryRows, ...packageRows.flat()], identity, nowMs,
|
|
766
|
+
);
|
|
767
|
+
if (expiring) {
|
|
768
|
+
secondary.expiring_at = expiring.at;
|
|
769
|
+
secondary.expiring_first_at = expiring.firstAt;
|
|
770
|
+
secondary.expiring_credits = expiring.credits;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
684
773
|
return {
|
|
685
774
|
configured: true, error: null,
|
|
686
775
|
plan_label: quota?.plan_label || englishPlanLabel({ code: identity.code }, identity, variantId),
|
|
687
776
|
primary_window: quota?.primary_window || null,
|
|
688
|
-
secondary_window:
|
|
777
|
+
secondary_window: secondary,
|
|
689
778
|
};
|
|
690
779
|
}
|
|
691
780
|
|
|
@@ -724,7 +813,8 @@ async function fetchVariantLive(variant, variantId, { env, workbuddyHome, instal
|
|
|
724
813
|
discovery, signal: opts.signal, timeoutMs: opts.timeoutMs, connectImpl: opts.connectImpl,
|
|
725
814
|
});
|
|
726
815
|
try {
|
|
727
|
-
return { ...await fetchPlanQuota((apiPath, body) => client.postBilling(apiPath, body), variantId
|
|
816
|
+
return { ...await fetchPlanQuota((apiPath, body) => client.postBilling(apiPath, body), variantId,
|
|
817
|
+
Number.isFinite(opts.nowMs) ? opts.nowMs : undefined), source: "ipc" };
|
|
728
818
|
} finally {
|
|
729
819
|
client.close();
|
|
730
820
|
}
|
|
@@ -748,7 +838,8 @@ async function fetchVariantLive(variant, variantId, { env, workbuddyHome, instal
|
|
|
748
838
|
throw error;
|
|
749
839
|
}
|
|
750
840
|
return { ...await fetchPlanQuota((apiPath, body) => postBilling(fetchImpl,
|
|
751
|
-
`${origin}${apiPath}`, session.token, body, opts.signal), variantId
|
|
841
|
+
`${origin}${apiPath}`, session.token, body, opts.signal), variantId,
|
|
842
|
+
Number.isFinite(opts.nowMs) ? opts.nowMs : undefined), source: "api" };
|
|
752
843
|
}
|
|
753
844
|
|
|
754
845
|
async function fetchVariant(variantId, opts = {}) {
|
|
@@ -817,6 +908,7 @@ module.exports = {
|
|
|
817
908
|
selectWorkbuddyLimits,
|
|
818
909
|
collectPackageRows,
|
|
819
910
|
collectResourceGroups,
|
|
911
|
+
collectExpiringResourceCredits,
|
|
820
912
|
resourceWindow,
|
|
821
913
|
readSubscriptionIdentity,
|
|
822
914
|
originAllowed,
|