ccstatusline 2.2.25 โ†’ 2.2.26

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 CHANGED
@@ -47,6 +47,14 @@
47
47
 
48
48
  ## ๐Ÿ†• Recent Updates
49
49
 
50
+ ### v2.2.25 - v2.2.26 - Fable usage, migrated usage API support, compaction accuracy, and rendering reliability
51
+
52
+ - **๐Ÿช„ Weekly Fable usage** - Added a `Weekly Fable Usage` widget with percentage, progress-bar, remaining-mode, and time-cursor controls.
53
+ - **๐Ÿ“Š Reshaped usage API support** - Session and all-model weekly usage can fall back to the newer `limits[]` response, while Sonnet, Opus, and Fable weekly widgets read authoritative `weekly_scoped` entries so migrated accounts do not show stale or frozen values.
54
+ - **๐Ÿง  Correct post-compaction context** - Context length and percentage widgets reset immediately from `compact_boundary.postTokens` after `/compact`, then switch to the first new turn instead of retaining the pre-compaction size.
55
+ - **๐Ÿงน Reliable hidden-widget separators** - Manual separators now look past widgets that render empty, preserve the intended visible boundary, and inherit colors from the actual preceding visible widget.
56
+ - **โšก Non-blocking Git PR/CI refreshes** - Git PR and CI widgets render from a versioned disk cache while stale data refreshes in the background, preventing slow `gh` calls from blocking the status line.
57
+
50
58
  ### v2.2.22 - v2.2.24 - Powerline flex mode, cache/CI/sandbox visibility, layout controls, composable metrics, and safer config
51
59
 
52
60
  ![Powerline Flex Mode](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/powerline-flex.png)
@@ -258,7 +266,7 @@
258
266
 
259
267
  ## โœจ Features
260
268
 
261
- - **๐Ÿ“Š Real-time Metrics** - Display model name, git branch, token usage, per-model weekly usage, extra usage limits, voice input state, session duration, compaction count, block timer, and more
269
+ - **๐Ÿ“Š Real-time Metrics** - Display model name, git branch, token usage, Sonnet/Opus/Fable weekly usage, extra usage limits, voice input state, session duration, compaction count, block timer, and more
262
270
  - **๐ŸŽจ Fully Customizable** - Choose what to display and customize colors for each element
263
271
  - **โšก Powerline Support** - Beautiful Powerline-style rendering with arrow separators, caps, and custom fonts
264
272
  - **๐Ÿ“ Multi-line Support** - Configure multiple independent status lines
@@ -58287,7 +58287,7 @@ function getTerminalWidth() {
58287
58287
  function canDetectTerminalWidth() {
58288
58288
  return probeTerminalWidth() !== null;
58289
58289
  }
58290
- var __dirname = "/home/runner/work/ccstatusline/ccstatusline/src/utils", PACKAGE_VERSION = "2.2.25";
58290
+ var __dirname = "/home/runner/work/ccstatusline/ccstatusline/src/utils", PACKAGE_VERSION = "2.2.26";
58291
58291
  var init_terminal = () => {};
58292
58292
 
58293
58293
  // src/utils/format-tokens.ts
@@ -58761,6 +58761,9 @@ function formatSeparator(sep) {
58761
58761
  }
58762
58762
  return sep;
58763
58763
  }
58764
+ function isSpacingSeparator(widget, defaultSeparator) {
58765
+ return widget?.type === "separator" && (widget.character ?? defaultSeparator ?? "|").trim().length === 0;
58766
+ }
58764
58767
  function countPowerlineStartCapSlots(widgets, preRenderedWidgets) {
58765
58768
  let pendingFlexAfterRenderedSegment = false;
58766
58769
  let hasRenderedSegment = false;
@@ -58914,27 +58917,44 @@ function renderStatusLine(widgets, settings, context, preRenderedWidgets, preCal
58914
58917
  if (!widget)
58915
58918
  continue;
58916
58919
  if (widget.type === "separator") {
58917
- let hasContentBefore = false;
58920
+ let contentBeforeIndex = null;
58921
+ let replacesSpacingSeparator = false;
58922
+ let crossedEmptyWidget = false;
58918
58923
  for (let j = i - 1;j >= 0; j--) {
58919
58924
  const prevWidget = widgets[j];
58920
58925
  if (!prevWidget)
58921
58926
  continue;
58922
- if (prevWidget.type === "separator" || prevWidget.type === "flex-separator")
58927
+ if (prevWidget.type === "separator") {
58928
+ if (!isSpacingSeparator(prevWidget, settings.defaultSeparator))
58929
+ break;
58930
+ replacesSpacingSeparator = true;
58923
58931
  continue;
58924
- hasContentBefore = Boolean(preRenderedWidgets[j]?.content);
58925
- break;
58932
+ }
58933
+ if (prevWidget.type === "flex-separator")
58934
+ break;
58935
+ if (preRenderedWidgets[j]?.content) {
58936
+ if (!prevWidget.merge || !crossedEmptyWidget)
58937
+ contentBeforeIndex = j;
58938
+ break;
58939
+ }
58940
+ crossedEmptyWidget = true;
58926
58941
  }
58927
- if (!hasContentBefore)
58942
+ if (contentBeforeIndex === null)
58928
58943
  continue;
58944
+ if (replacesSpacingSeparator) {
58945
+ while (isSpacingSeparator(elements[elements.length - 1]?.widget, settings.defaultSeparator)) {
58946
+ elements.pop();
58947
+ }
58948
+ }
58929
58949
  const sepChar = widget.character ?? (settings.defaultSeparator ?? "|");
58930
58950
  const formattedSep = formatSeparator(sepChar);
58931
58951
  let separatorColor = widget.color ?? "gray";
58932
58952
  let separatorBg = widget.backgroundColor;
58933
58953
  let separatorBold = widget.bold;
58934
58954
  let separatorDim = widget.dim;
58935
- if (settings.inheritSeparatorColors && i > 0 && !widget.color && !widget.backgroundColor) {
58936
- const prevWidget = widgets[i - 1];
58937
- if (prevWidget && prevWidget.type !== "separator" && prevWidget.type !== "flex-separator") {
58955
+ if (settings.inheritSeparatorColors && !widget.color && !widget.backgroundColor) {
58956
+ const prevWidget = widgets[contentBeforeIndex];
58957
+ if (prevWidget) {
58938
58958
  let widgetColor = prevWidget.color;
58939
58959
  if (!widgetColor) {
58940
58960
  const widgetImpl = getWidget(prevWidget.type);
@@ -61880,12 +61900,20 @@ var init_dist5 = __esm(() => {
61880
61900
  });
61881
61901
 
61882
61902
  // src/utils/usage-types.ts
61883
- var FIVE_HOUR_BLOCK_MS, SEVEN_DAY_WINDOW_MS, UsageErrorSchema;
61903
+ function setUsageField(target, field, value) {
61904
+ target[field] = value;
61905
+ }
61906
+ var FIVE_HOUR_BLOCK_MS, SEVEN_DAY_WINDOW_MS, UsageErrorSchema, WEEKLY_MODEL_USAGE_BUCKETS;
61884
61907
  var init_usage_types = __esm(() => {
61885
61908
  init_zod();
61886
61909
  FIVE_HOUR_BLOCK_MS = 5 * 60 * 60 * 1000;
61887
61910
  SEVEN_DAY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
61888
61911
  UsageErrorSchema = exports_external.enum(["no-credentials", "timeout", "rate-limited", "api-error", "parse-error"]);
61912
+ WEEKLY_MODEL_USAGE_BUCKETS = [
61913
+ { widgetType: "weekly-sonnet-usage", modelDisplayName: "Sonnet", apiBucketKey: "seven_day_sonnet", usageField: "weeklySonnetUsage", resetField: "weeklySonnetResetAt" },
61914
+ { widgetType: "weekly-opus-usage", modelDisplayName: "Opus", apiBucketKey: "seven_day_opus", usageField: "weeklyOpusUsage", resetField: "weeklyOpusResetAt" },
61915
+ { widgetType: "fable-weekly-usage", modelDisplayName: "Fable", usageField: "fableUsage", resetField: "fableResetAt" }
61916
+ ];
61889
61917
  });
61890
61918
 
61891
61919
  // src/utils/usage-fetch.ts
@@ -61898,6 +61926,22 @@ import * as path4 from "path";
61898
61926
  function getUsageApiBucketUtilization(bucket) {
61899
61927
  return bucket === null ? 0 : bucket?.utilization ?? undefined;
61900
61928
  }
61929
+ function findUsageApiLimit(limits, kind) {
61930
+ return limits?.find((limit) => limit.kind === kind);
61931
+ }
61932
+ function isPlaceholderUsageApiLimit(limit) {
61933
+ return (limit.percent ?? 0) === 0 && (limit.resets_at ?? null) === null;
61934
+ }
61935
+ function getUsageApiLimitPercent(limit) {
61936
+ return limit && !isPlaceholderUsageApiLimit(limit) ? limit.percent ?? undefined : undefined;
61937
+ }
61938
+ function getUsageApiLimitResetAt(limit) {
61939
+ return limit && !isPlaceholderUsageApiLimit(limit) ? limit.resets_at ?? undefined : undefined;
61940
+ }
61941
+ function findWeeklyScopedLimit(limits, modelDisplayName) {
61942
+ const normalizedModelName = modelDisplayName.toLowerCase();
61943
+ return limits?.find((limit) => limit.kind === "weekly_scoped" && (limit.scope?.model?.display_name ?? "").toLowerCase().includes(normalizedModelName));
61944
+ }
61901
61945
  function parseJsonWithSchema(rawJson, schema) {
61902
61946
  try {
61903
61947
  const parsed = schema.safeParse(JSON.parse(rawJson));
@@ -61925,6 +61969,8 @@ function parseCachedUsageData(rawJson) {
61925
61969
  weeklySonnetResetAt: parsed.weeklySonnetResetAt ?? undefined,
61926
61970
  weeklyOpusUsage: parsed.weeklyOpusUsage ?? undefined,
61927
61971
  weeklyOpusResetAt: parsed.weeklyOpusResetAt ?? undefined,
61972
+ fableUsage: parsed.fableUsage ?? undefined,
61973
+ fableResetAt: parsed.fableResetAt ?? undefined,
61928
61974
  extraUsageEnabled: parsed.extraUsageEnabled ?? undefined,
61929
61975
  extraUsageLimit: parsed.extraUsageLimit ?? undefined,
61930
61976
  extraUsageUsed: parsed.extraUsageUsed ?? undefined,
@@ -61945,26 +61991,34 @@ function tokenHashMatches(cachedHash, currentHash) {
61945
61991
  }
61946
61992
  return cachedHash === currentHash;
61947
61993
  }
61994
+ function getLegacyUsageApiBucket(parsed, apiBucketKey) {
61995
+ return parsed[apiBucketKey];
61996
+ }
61948
61997
  function parseUsageApiResponse(rawJson) {
61949
61998
  const parsed = parseJsonWithSchema(rawJson, UsageApiResponseSchema);
61950
61999
  if (!parsed) {
61951
62000
  return null;
61952
62001
  }
61953
- return {
61954
- sessionUsage: getUsageApiBucketUtilization(parsed.five_hour),
61955
- sessionResetAt: parsed.five_hour?.resets_at ?? undefined,
61956
- weeklyUsage: getUsageApiBucketUtilization(parsed.seven_day),
61957
- weeklyResetAt: parsed.seven_day?.resets_at ?? undefined,
61958
- weeklySonnetUsage: getUsageApiBucketUtilization(parsed.seven_day_sonnet),
61959
- weeklySonnetResetAt: parsed.seven_day_sonnet?.resets_at ?? undefined,
61960
- weeklyOpusUsage: getUsageApiBucketUtilization(parsed.seven_day_opus),
61961
- weeklyOpusResetAt: parsed.seven_day_opus?.resets_at ?? undefined,
62002
+ const sessionLimit = findUsageApiLimit(parsed.limits, "session");
62003
+ const weeklyLimit = findUsageApiLimit(parsed.limits, "weekly_all");
62004
+ const result2 = {
62005
+ sessionUsage: getUsageApiBucketUtilization(parsed.five_hour) ?? getUsageApiLimitPercent(sessionLimit),
62006
+ sessionResetAt: parsed.five_hour?.resets_at ?? getUsageApiLimitResetAt(sessionLimit),
62007
+ weeklyUsage: getUsageApiBucketUtilization(parsed.seven_day) ?? getUsageApiLimitPercent(weeklyLimit),
62008
+ weeklyResetAt: parsed.seven_day?.resets_at ?? getUsageApiLimitResetAt(weeklyLimit),
61962
62009
  extraUsageEnabled: parsed.extra_usage?.is_enabled ?? undefined,
61963
62010
  extraUsageLimit: parsed.extra_usage?.monthly_limit ?? undefined,
61964
62011
  extraUsageUsed: parsed.extra_usage?.used_credits ?? undefined,
61965
62012
  extraUsageUtilization: parsed.extra_usage?.utilization ?? undefined,
61966
62013
  extraUsageCurrency: parsed.extra_usage?.currency ?? undefined
61967
62014
  };
62015
+ for (const bucket of WEEKLY_MODEL_USAGE_BUCKETS) {
62016
+ const scopedLimit = findWeeklyScopedLimit(parsed.limits, bucket.modelDisplayName);
62017
+ const legacyBucket = bucket.apiBucketKey ? getLegacyUsageApiBucket(parsed, bucket.apiBucketKey) : undefined;
62018
+ setUsageField(result2, bucket.usageField, getUsageApiLimitPercent(scopedLimit) ?? getUsageApiBucketUtilization(legacyBucket));
62019
+ setUsageField(result2, bucket.resetField, getUsageApiLimitResetAt(scopedLimit) ?? legacyBucket?.resets_at ?? undefined);
62020
+ }
62021
+ return result2;
61968
62022
  }
61969
62023
  function ensureCacheDirExists() {
61970
62024
  if (!fs4.existsSync(CACHE_DIR)) {
@@ -61992,7 +62046,10 @@ function hasRequiredUsageField(data, field) {
61992
62046
  if (windowSentinel !== undefined && data[windowSentinel] !== undefined) {
61993
62047
  return true;
61994
62048
  }
61995
- return data.extraUsageEnabled !== undefined && EXTRA_USAGE_DETAIL_FIELDS.has(field);
62049
+ if (data.extraUsageEnabled !== undefined && EXTRA_USAGE_DETAIL_FIELDS.has(field)) {
62050
+ return true;
62051
+ }
62052
+ return (data.sessionUsage !== undefined || data.weeklyUsage !== undefined) && FABLE_USAGE_FIELDS.has(field);
61996
62053
  }
61997
62054
  function hasRequiredUsageFields(data, requiredFields = []) {
61998
62055
  return requiredFields.every((field) => hasRequiredUsageField(data, field));
@@ -62325,7 +62382,7 @@ async function fetchUsageData(options = {}) {
62325
62382
  return getStaleUsageOrError("parse-error", now2, currentTokenHash, LOCK_MAX_AGE, requiredFields);
62326
62383
  }
62327
62384
  }
62328
- var CACHE_DIR, CACHE_FILE, LOCK_FILE, CACHE_MAX_AGE = 180, LOCK_MAX_AGE = 30, DEFAULT_RATE_LIMIT_BACKOFF = 300, MACOS_USAGE_CREDENTIALS_SERVICE = "Claude Code-credentials", MACOS_SECURITY_DUMP_MAX_BUFFER, EXTRA_USAGE_DETAIL_FIELDS, WINDOW_RESET_FIELD_SENTINELS, UsageCredentialsSchema, UsageLockErrorSchema, UsageLockSchema, CachedUsageDataSchema, CachedTokenHashSchema, UsageApiBucketSchema, UsageApiResponseSchema, cachedUsageData = null, usageCacheTime = 0, usageErrorCacheMaxAge, USAGE_API_HOST = "api.anthropic.com", USAGE_API_PATH = "/api/oauth/usage", USAGE_API_TIMEOUT_MS = 5000;
62385
+ var CACHE_DIR, CACHE_FILE, LOCK_FILE, CACHE_MAX_AGE = 180, LOCK_MAX_AGE = 30, DEFAULT_RATE_LIMIT_BACKOFF = 300, MACOS_USAGE_CREDENTIALS_SERVICE = "Claude Code-credentials", MACOS_SECURITY_DUMP_MAX_BUFFER, EXTRA_USAGE_DETAIL_FIELDS, FABLE_USAGE_FIELDS, WINDOW_RESET_FIELD_SENTINELS, UsageCredentialsSchema, UsageLockErrorSchema, UsageLockSchema, CachedUsageDataSchema, CachedTokenHashSchema, UsageApiBucketSchema, UsageApiLimitSchema, UsageApiResponseSchema, cachedUsageData = null, usageCacheTime = 0, usageErrorCacheMaxAge, USAGE_API_HOST = "api.anthropic.com", USAGE_API_PATH = "/api/oauth/usage", USAGE_API_TIMEOUT_MS = 5000;
62329
62386
  var init_usage_fetch = __esm(async () => {
62330
62387
  init_dist5();
62331
62388
  init_zod();
@@ -62340,11 +62397,14 @@ var init_usage_fetch = __esm(async () => {
62340
62397
  "extraUsageUsed",
62341
62398
  "extraUsageUtilization"
62342
62399
  ]);
62400
+ FABLE_USAGE_FIELDS = new Set([
62401
+ "fableUsage",
62402
+ "fableResetAt"
62403
+ ]);
62343
62404
  WINDOW_RESET_FIELD_SENTINELS = {
62344
62405
  sessionResetAt: "sessionUsage",
62345
62406
  weeklyResetAt: "weeklyUsage",
62346
- weeklySonnetResetAt: "weeklySonnetUsage",
62347
- weeklyOpusResetAt: "weeklyOpusUsage"
62407
+ ...Object.fromEntries(WEEKLY_MODEL_USAGE_BUCKETS.map((bucket) => [bucket.resetField, bucket.usageField]))
62348
62408
  };
62349
62409
  UsageCredentialsSchema = exports_external.object({ claudeAiOauth: exports_external.object({ accessToken: exports_external.string().nullable().optional() }).optional() });
62350
62410
  UsageLockErrorSchema = exports_external.enum(["timeout", "rate-limited", "parse-error"]);
@@ -62361,6 +62421,8 @@ var init_usage_fetch = __esm(async () => {
62361
62421
  weeklySonnetResetAt: exports_external.string().nullable().optional(),
62362
62422
  weeklyOpusUsage: exports_external.number().nullable().optional(),
62363
62423
  weeklyOpusResetAt: exports_external.string().nullable().optional(),
62424
+ fableUsage: exports_external.number().nullable().optional(),
62425
+ fableResetAt: exports_external.string().nullable().optional(),
62364
62426
  extraUsageEnabled: exports_external.boolean().nullable().optional(),
62365
62427
  extraUsageLimit: exports_external.number().nullable().optional(),
62366
62428
  extraUsageUsed: exports_external.number().nullable().optional(),
@@ -62373,11 +62435,18 @@ var init_usage_fetch = __esm(async () => {
62373
62435
  utilization: exports_external.number().nullable().optional(),
62374
62436
  resets_at: exports_external.string().nullable().optional()
62375
62437
  }).nullable().optional();
62438
+ UsageApiLimitSchema = exports_external.looseObject({
62439
+ kind: exports_external.string().nullable().optional(),
62440
+ percent: exports_external.number().nullable().optional(),
62441
+ resets_at: exports_external.string().nullable().optional(),
62442
+ scope: exports_external.looseObject({ model: exports_external.looseObject({ display_name: exports_external.string().nullable().optional() }).nullable().optional() }).nullable().catch(null).optional()
62443
+ });
62376
62444
  UsageApiResponseSchema = exports_external.looseObject({
62377
62445
  five_hour: UsageApiBucketSchema,
62378
62446
  seven_day: UsageApiBucketSchema,
62379
62447
  seven_day_sonnet: UsageApiBucketSchema,
62380
62448
  seven_day_opus: UsageApiBucketSchema,
62449
+ limits: exports_external.array(UsageApiLimitSchema).nullable().optional(),
62381
62450
  extra_usage: exports_external.looseObject({
62382
62451
  is_enabled: exports_external.boolean().nullable().optional(),
62383
62452
  monthly_limit: exports_external.number().nullable().optional(),
@@ -65133,8 +65202,79 @@ var init_jsonl_cache = __esm(async () => {
65133
65202
  existsSync6 = fs7.existsSync;
65134
65203
  });
65135
65204
 
65136
- // src/utils/jsonl-metrics.ts
65205
+ // src/utils/compaction.ts
65137
65206
  import * as fs8 from "fs";
65207
+ function isCompactBoundary(record2) {
65208
+ if (typeof record2 !== "object" || record2 === null) {
65209
+ return false;
65210
+ }
65211
+ const r = record2;
65212
+ return r.type === "system" && r.subtype === "compact_boundary" && r.isSidechain !== true;
65213
+ }
65214
+ function getCompactBoundaryPostTokens(record2) {
65215
+ if (!isCompactBoundary(record2)) {
65216
+ return null;
65217
+ }
65218
+ const meta3 = record2.compactMetadata;
65219
+ const post = typeof meta3 === "object" && meta3 !== null ? meta3.postTokens : undefined;
65220
+ return typeof post === "number" && Number.isFinite(post) ? Math.max(0, post) : null;
65221
+ }
65222
+ function computeCompactionStats(lines) {
65223
+ const stats = {
65224
+ count: 0,
65225
+ byTrigger: { auto: 0, manual: 0, unknown: 0 },
65226
+ tokensReclaimed: 0
65227
+ };
65228
+ for (const line of lines) {
65229
+ const record2 = parseJsonlLine(line);
65230
+ if (!isCompactBoundary(record2)) {
65231
+ continue;
65232
+ }
65233
+ stats.count += 1;
65234
+ const meta3 = record2.compactMetadata;
65235
+ const metaRecord = typeof meta3 === "object" && meta3 !== null ? meta3 : null;
65236
+ const trigger = metaRecord?.trigger;
65237
+ if (trigger === "auto") {
65238
+ stats.byTrigger.auto += 1;
65239
+ } else if (trigger === "manual") {
65240
+ stats.byTrigger.manual += 1;
65241
+ } else {
65242
+ stats.byTrigger.unknown += 1;
65243
+ }
65244
+ const pre = metaRecord?.preTokens;
65245
+ const post = metaRecord?.postTokens;
65246
+ if (typeof pre === "number" && typeof post === "number") {
65247
+ const reclaimed = pre - post;
65248
+ if (Number.isFinite(reclaimed)) {
65249
+ stats.tokensReclaimed += Math.max(0, reclaimed);
65250
+ }
65251
+ }
65252
+ }
65253
+ return stats;
65254
+ }
65255
+ async function getCompactionStats(transcriptPath) {
65256
+ try {
65257
+ if (!fs8.existsSync(transcriptPath)) {
65258
+ return ZERO_COMPACTION_STATS;
65259
+ }
65260
+ const lines = await readJsonlLines(transcriptPath);
65261
+ return computeCompactionStats(lines);
65262
+ } catch {
65263
+ return ZERO_COMPACTION_STATS;
65264
+ }
65265
+ }
65266
+ var ZERO_COMPACTION_STATS;
65267
+ var init_compaction = __esm(() => {
65268
+ init_jsonl_lines();
65269
+ ZERO_COMPACTION_STATS = Object.freeze({
65270
+ count: 0,
65271
+ byTrigger: Object.freeze({ auto: 0, manual: 0, unknown: 0 }),
65272
+ tokensReclaimed: 0
65273
+ });
65274
+ });
65275
+
65276
+ // src/utils/jsonl-metrics.ts
65277
+ import * as fs9 from "fs";
65138
65278
  import path7 from "node:path";
65139
65279
  function collectAgentIds(value, agentIds) {
65140
65280
  if (!value || typeof value !== "object") {
@@ -65167,7 +65307,7 @@ function getReferencedSubagentIds(lines) {
65167
65307
  }
65168
65308
  async function getSessionDuration(transcriptPath) {
65169
65309
  try {
65170
- if (!fs8.existsSync(transcriptPath)) {
65310
+ if (!fs9.existsSync(transcriptPath)) {
65171
65311
  return null;
65172
65312
  }
65173
65313
  const lines = await readJsonlLines(transcriptPath);
@@ -65217,7 +65357,7 @@ async function getSessionDuration(transcriptPath) {
65217
65357
  }
65218
65358
  async function getTokenMetrics(transcriptPath) {
65219
65359
  try {
65220
- if (!fs8.existsSync(transcriptPath)) {
65360
+ if (!fs9.existsSync(transcriptPath)) {
65221
65361
  return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0, totalTokens: 0, contextLength: 0 };
65222
65362
  }
65223
65363
  const lines = await readJsonlLines(transcriptPath);
@@ -65228,22 +65368,30 @@ async function getTokenMetrics(transcriptPath) {
65228
65368
  let contextLength = 0;
65229
65369
  let mostRecentMainChainEntry = null;
65230
65370
  let mostRecentTimestamp = null;
65371
+ let mostRecentPostCompactionEntry = null;
65372
+ let mostRecentPostCompactionTimestamp = null;
65373
+ let lastCompactBoundaryLineIndex = -1;
65374
+ let lastCompactBoundaryPostTokens = null;
65231
65375
  const parsedEntries = [];
65232
65376
  let hasStopReasonField = false;
65233
- for (const line of lines) {
65377
+ for (const [lineIndex, line] of lines.entries()) {
65234
65378
  const data = parseJsonlLine(line);
65379
+ if (isCompactBoundary(data)) {
65380
+ lastCompactBoundaryLineIndex = lineIndex;
65381
+ lastCompactBoundaryPostTokens = getCompactBoundaryPostTokens(data);
65382
+ }
65235
65383
  if (data?.message?.usage) {
65236
- parsedEntries.push(data);
65384
+ parsedEntries.push({ data, lineIndex });
65237
65385
  if (Object.hasOwn(data.message, "stop_reason")) {
65238
65386
  hasStopReasonField = true;
65239
65387
  }
65240
65388
  }
65241
65389
  }
65242
- const entriesToCount = hasStopReasonField ? parsedEntries.filter((data, index) => {
65243
- const stopReason = data.message?.stop_reason;
65390
+ const entriesToCount = hasStopReasonField ? parsedEntries.filter((entry, index) => {
65391
+ const stopReason = entry.data.message?.stop_reason;
65244
65392
  return Boolean(stopReason) || stopReason === null && index === parsedEntries.length - 1;
65245
65393
  }) : parsedEntries;
65246
- for (const data of entriesToCount) {
65394
+ for (const { data, lineIndex } of entriesToCount) {
65247
65395
  const usage = data.message?.usage;
65248
65396
  if (!usage) {
65249
65397
  continue;
@@ -65258,12 +65406,20 @@ async function getTokenMetrics(transcriptPath) {
65258
65406
  mostRecentTimestamp = entryTime;
65259
65407
  mostRecentMainChainEntry = data;
65260
65408
  }
65409
+ if (lineIndex > lastCompactBoundaryLineIndex && (!mostRecentPostCompactionTimestamp || entryTime > mostRecentPostCompactionTimestamp)) {
65410
+ mostRecentPostCompactionTimestamp = entryTime;
65411
+ mostRecentPostCompactionEntry = data;
65412
+ }
65261
65413
  }
65262
65414
  }
65263
- if (mostRecentMainChainEntry?.message?.usage) {
65264
- const usage = mostRecentMainChainEntry.message.usage;
65265
- contextLength = (usage.input_tokens || 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
65266
- }
65415
+ const contextLengthFromEntry = (entry) => {
65416
+ const usage = entry?.message?.usage;
65417
+ if (!usage) {
65418
+ return null;
65419
+ }
65420
+ return (usage.input_tokens || 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
65421
+ };
65422
+ contextLength = lastCompactBoundaryLineIndex >= 0 ? contextLengthFromEntry(mostRecentPostCompactionEntry) ?? lastCompactBoundaryPostTokens ?? 0 : contextLengthFromEntry(mostRecentMainChainEntry) ?? 0;
65267
65423
  const cachedTokens = cacheReadTokens + cacheCreationTokens;
65268
65424
  const totalTokens = inputTokens + outputTokens + cachedTokens;
65269
65425
  return { inputTokens, outputTokens, cachedTokens, cacheReadTokens, cacheCreationTokens, totalTokens, contextLength };
@@ -65442,11 +65598,11 @@ function getSubagentTranscriptPaths(transcriptPath, referencedAgentIds) {
65442
65598
  const seenPaths = new Set;
65443
65599
  const matchedPaths = [];
65444
65600
  for (const subagentsDir of candidateDirs) {
65445
- if (!fs8.existsSync(subagentsDir)) {
65601
+ if (!fs9.existsSync(subagentsDir)) {
65446
65602
  continue;
65447
65603
  }
65448
65604
  try {
65449
- const dirEntries = fs8.readdirSync(subagentsDir, { withFileTypes: true });
65605
+ const dirEntries = fs9.readdirSync(subagentsDir, { withFileTypes: true });
65450
65606
  for (const entry of dirEntries) {
65451
65607
  if (!entry.isFile()) {
65452
65608
  continue;
@@ -65475,7 +65631,7 @@ async function getSpeedMetricsCollection(transcriptPath, options = {}) {
65475
65631
  const normalizedWindows = Array.from(new Set((options.windowSeconds ?? []).map((window2) => normalizeWindowSeconds(window2)).filter((window2) => window2 !== null)));
65476
65632
  const emptyWindowedMetrics = buildEmptyWindowedMetrics(normalizedWindows);
65477
65633
  try {
65478
- if (!fs8.existsSync(transcriptPath)) {
65634
+ if (!fs9.existsSync(transcriptPath)) {
65479
65635
  return {
65480
65636
  sessionAverage: createEmptySpeedMetrics(),
65481
65637
  windowed: emptyWindowedMetrics
@@ -65520,6 +65676,7 @@ async function getSpeedMetricsCollection(transcriptPath, options = {}) {
65520
65676
  }
65521
65677
  }
65522
65678
  var init_jsonl_metrics = __esm(() => {
65679
+ init_compaction();
65523
65680
  init_jsonl_lines();
65524
65681
  });
65525
65682
 
@@ -65658,6 +65815,9 @@ function resolveWeeklySonnetUsageWindow(usageData, nowMs = Date.now()) {
65658
65815
  function resolveWeeklyOpusUsageWindow(usageData, nowMs = Date.now()) {
65659
65816
  return getWeeklyUsageWindowFromResetAt(usageData.weeklyOpusResetAt ?? usageData.weeklyResetAt, nowMs);
65660
65817
  }
65818
+ function resolveFableUsageWindow(usageData, nowMs = Date.now()) {
65819
+ return getWeeklyUsageWindowFromResetAt(usageData.fableResetAt ?? usageData.weeklyResetAt, nowMs);
65820
+ }
65661
65821
  function formatUsageDuration(durationMs, compact2 = false, useDays = true) {
65662
65822
  const clampedMs = Math.max(0, durationMs);
65663
65823
  const totalHours = Math.floor(clampedMs / (1000 * 60 * 60));
@@ -66872,7 +67032,7 @@ var init_JjRevision = __esm(() => {
66872
67032
  });
66873
67033
 
66874
67034
  // src/widgets/ClaudeAccountEmail.ts
66875
- import * as fs9 from "fs";
67035
+ import * as fs10 from "fs";
66876
67036
 
66877
67037
  class ClaudeAccountEmailWidget {
66878
67038
  getDefaultColor() {
@@ -66895,7 +67055,7 @@ class ClaudeAccountEmailWidget {
66895
67055
  return item.rawValue ? "you@example.com" : "Account: you@example.com";
66896
67056
  }
66897
67057
  try {
66898
- const content = fs9.readFileSync(getClaudeJsonPath(), "utf-8");
67058
+ const content = fs10.readFileSync(getClaudeJsonPath(), "utf-8");
66899
67059
  const data = JSON.parse(content);
66900
67060
  const email3 = data.oauthAccount?.emailAddress;
66901
67061
  if (typeof email3 !== "string" || email3.length === 0) {
@@ -67333,7 +67493,7 @@ class FreeMemoryWidget {
67333
67493
  var init_FreeMemory = () => {};
67334
67494
 
67335
67495
  // src/widgets/SessionName.ts
67336
- import * as fs10 from "fs";
67496
+ import * as fs11 from "fs";
67337
67497
 
67338
67498
  class SessionNameWidget {
67339
67499
  getDefaultColor() {
@@ -67360,7 +67520,7 @@ class SessionNameWidget {
67360
67520
  return null;
67361
67521
  }
67362
67522
  try {
67363
- const content = fs10.readFileSync(transcriptPath, "utf-8");
67523
+ const content = fs11.readFileSync(transcriptPath, "utf-8");
67364
67524
  const lines = content.split(`
67365
67525
  `);
67366
67526
  for (let i = lines.length - 1;i >= 0; i--) {
@@ -68039,6 +68199,102 @@ var init_WeeklyOpusUsage = __esm(async () => {
68039
68199
  await init_usage();
68040
68200
  });
68041
68201
 
68202
+ // src/widgets/FableWeeklyUsage.ts
68203
+ class FableWeeklyUsageWidget {
68204
+ getDefaultColor() {
68205
+ return "brightBlue";
68206
+ }
68207
+ getDescription() {
68208
+ return "Shows Fable-only weekly usage percentage";
68209
+ }
68210
+ getDisplayName() {
68211
+ return "Weekly Fable Usage";
68212
+ }
68213
+ getCategory() {
68214
+ return "Usage";
68215
+ }
68216
+ getEditorDisplay(item) {
68217
+ return {
68218
+ displayText: this.getDisplayName(),
68219
+ modifierText: getUsageDisplayModifierText(item, { showUsageDirection: true })
68220
+ };
68221
+ }
68222
+ handleEditorAction(action, item) {
68223
+ if (action === "toggle-progress") {
68224
+ return cycleUsageDisplayMode(item, [], true, true);
68225
+ }
68226
+ if (action === "toggle-invert") {
68227
+ return toggleUsageInverted(item);
68228
+ }
68229
+ if (action === "toggle-cursor") {
68230
+ return toggleUsageCursor(item);
68231
+ }
68232
+ return null;
68233
+ }
68234
+ render(item, context, settings) {
68235
+ const displayMode = getUsageDisplayMode(item);
68236
+ const inverted = isUsageInverted(item);
68237
+ const showCursor = isUsageCursorEnabled(item);
68238
+ if (context.isPreview) {
68239
+ const previewPercent = 4;
68240
+ const renderedPercent2 = inverted ? 100 - previewPercent : previewPercent;
68241
+ if (isUsageProgressMode(displayMode)) {
68242
+ const width = getUsageProgressBarWidth(displayMode);
68243
+ const progressBar = makeTimerProgressBar2(renderedPercent2, width, showCursor ? { cursorPercent: 50 } : undefined);
68244
+ const progressDisplay = `[${progressBar}] ${renderedPercent2.toFixed(1)}%`;
68245
+ return formatRawOrLabeledValue(item, LABEL3, progressDisplay);
68246
+ }
68247
+ if (isUsageSliderMode(displayMode)) {
68248
+ const slider = makeSliderBar(renderedPercent2, undefined, showCursor ? { cursorPercent: 50 } : undefined);
68249
+ const sliderDisplay = displayMode === "slider" ? `${slider} ${renderedPercent2.toFixed(1)}%` : slider;
68250
+ return formatRawOrLabeledValue(item, LABEL3, sliderDisplay);
68251
+ }
68252
+ return formatRawOrLabeledValue(item, LABEL3, `${renderedPercent2.toFixed(1)}%`);
68253
+ }
68254
+ const data = context.usageData ?? {};
68255
+ if (data.fableUsage === undefined) {
68256
+ if (data.error)
68257
+ return getUsageErrorMessage(data.error);
68258
+ return null;
68259
+ }
68260
+ const percent = Math.max(0, Math.min(100, data.fableUsage));
68261
+ const renderedPercent = inverted ? 100 - percent : percent;
68262
+ const getCursorOptions = () => {
68263
+ if (!showCursor) {
68264
+ return;
68265
+ }
68266
+ const window2 = resolveFableUsageWindow(data);
68267
+ return window2 ? { cursorPercent: window2.elapsedPercent } : undefined;
68268
+ };
68269
+ if (isUsageProgressMode(displayMode)) {
68270
+ const width = getUsageProgressBarWidth(displayMode);
68271
+ const progressBar = makeTimerProgressBar2(renderedPercent, width, getCursorOptions());
68272
+ const progressDisplay = `[${progressBar}] ${renderedPercent.toFixed(1)}%`;
68273
+ return formatRawOrLabeledValue(item, LABEL3, progressDisplay);
68274
+ }
68275
+ if (isUsageSliderMode(displayMode)) {
68276
+ const slider = makeSliderBar(renderedPercent, undefined, getCursorOptions());
68277
+ const sliderDisplay = displayMode === "slider" ? `${slider} ${renderedPercent.toFixed(1)}%` : slider;
68278
+ return formatRawOrLabeledValue(item, LABEL3, sliderDisplay);
68279
+ }
68280
+ return formatRawOrLabeledValue(item, LABEL3, `${renderedPercent.toFixed(1)}%`);
68281
+ }
68282
+ getCustomKeybinds(item) {
68283
+ return getUsagePercentCustomKeybinds(item);
68284
+ }
68285
+ supportsRawValue() {
68286
+ return true;
68287
+ }
68288
+ supportsColors(item) {
68289
+ return true;
68290
+ }
68291
+ }
68292
+ var LABEL3 = "Fable Weekly: ";
68293
+ var init_FableWeeklyUsage = __esm(async () => {
68294
+ init_usage_display();
68295
+ await init_usage();
68296
+ });
68297
+
68042
68298
  // src/widgets/shared/locale-editor.tsx
68043
68299
  function getInitialSelectedIndex(options, currentLocale) {
68044
68300
  const selectedValue = currentLocale ? canonicalizeLocale(currentLocale) : DEFAULT_RESET_LOCALE;
@@ -69675,69 +69931,6 @@ class GitWorktreeOriginalBranchWidget {
69675
69931
  }
69676
69932
  }
69677
69933
 
69678
- // src/utils/compaction.ts
69679
- import * as fs11 from "fs";
69680
- function isCompactBoundary(record2) {
69681
- if (typeof record2 !== "object" || record2 === null) {
69682
- return false;
69683
- }
69684
- const r = record2;
69685
- return r.type === "system" && r.subtype === "compact_boundary" && r.isSidechain !== true;
69686
- }
69687
- function computeCompactionStats(lines) {
69688
- const stats = {
69689
- count: 0,
69690
- byTrigger: { auto: 0, manual: 0, unknown: 0 },
69691
- tokensReclaimed: 0
69692
- };
69693
- for (const line of lines) {
69694
- const record2 = parseJsonlLine(line);
69695
- if (!isCompactBoundary(record2)) {
69696
- continue;
69697
- }
69698
- stats.count += 1;
69699
- const meta3 = record2.compactMetadata;
69700
- const metaRecord = typeof meta3 === "object" && meta3 !== null ? meta3 : null;
69701
- const trigger = metaRecord?.trigger;
69702
- if (trigger === "auto") {
69703
- stats.byTrigger.auto += 1;
69704
- } else if (trigger === "manual") {
69705
- stats.byTrigger.manual += 1;
69706
- } else {
69707
- stats.byTrigger.unknown += 1;
69708
- }
69709
- const pre = metaRecord?.preTokens;
69710
- const post = metaRecord?.postTokens;
69711
- if (typeof pre === "number" && typeof post === "number") {
69712
- const reclaimed = pre - post;
69713
- if (Number.isFinite(reclaimed)) {
69714
- stats.tokensReclaimed += Math.max(0, reclaimed);
69715
- }
69716
- }
69717
- }
69718
- return stats;
69719
- }
69720
- async function getCompactionStats(transcriptPath) {
69721
- try {
69722
- if (!fs11.existsSync(transcriptPath)) {
69723
- return ZERO_COMPACTION_STATS;
69724
- }
69725
- const lines = await readJsonlLines(transcriptPath);
69726
- return computeCompactionStats(lines);
69727
- } catch {
69728
- return ZERO_COMPACTION_STATS;
69729
- }
69730
- }
69731
- var ZERO_COMPACTION_STATS;
69732
- var init_compaction = __esm(() => {
69733
- init_jsonl_lines();
69734
- ZERO_COMPACTION_STATS = Object.freeze({
69735
- count: 0,
69736
- byTrigger: Object.freeze({ auto: 0, manual: 0, unknown: 0 }),
69737
- tokensReclaimed: 0
69738
- });
69739
- });
69740
-
69741
69934
  // src/widgets/CompactionCounter.ts
69742
69935
  function getFormat2(item) {
69743
69936
  const format = item.metadata?.format;
@@ -70707,6 +70900,7 @@ var init_widgets = __esm(async () => {
70707
70900
  init_ExtraUsageUsed(),
70708
70901
  init_WeeklySonnetUsage(),
70709
70902
  init_WeeklyOpusUsage(),
70903
+ init_FableWeeklyUsage(),
70710
70904
  init_BlockResetTimer(),
70711
70905
  init_WeeklyResetTimer(),
70712
70906
  init_ContextBar(),
@@ -70799,6 +70993,7 @@ var init_widget_manifest = __esm(async () => {
70799
70993
  { type: "extra-usage-used", create: () => new ExtraUsageUsedWidget },
70800
70994
  { type: "weekly-sonnet-usage", create: () => new WeeklySonnetUsageWidget },
70801
70995
  { type: "weekly-opus-usage", create: () => new WeeklyOpusUsageWidget },
70996
+ { type: "fable-weekly-usage", create: () => new FableWeeklyUsageWidget },
70802
70997
  { type: "reset-timer", create: () => new BlockResetTimerWidget },
70803
70998
  { type: "weekly-reset-timer", create: () => new WeeklyResetTimerWidget },
70804
70999
  { type: "context-bar", create: () => new ContextBarWidget },
@@ -80181,28 +80376,28 @@ await init_renderer2();
80181
80376
  init_terminal();
80182
80377
 
80183
80378
  // src/utils/usage-prefetch.ts
80379
+ init_usage_types();
80184
80380
  await init_usage();
80185
- var USAGE_WIDGET_TYPES = new Set([
80381
+ var BASE_USAGE_WIDGET_TYPES = [
80186
80382
  "session-usage",
80187
80383
  "weekly-usage",
80188
- "weekly-sonnet-usage",
80189
- "weekly-opus-usage",
80190
80384
  "block-timer",
80191
80385
  "reset-timer",
80192
80386
  "weekly-reset-timer",
80193
80387
  "extra-usage-utilization",
80194
80388
  "extra-usage-remaining",
80195
80389
  "extra-usage-used"
80390
+ ];
80391
+ var USAGE_WIDGET_TYPES = new Set([
80392
+ ...BASE_USAGE_WIDGET_TYPES,
80393
+ ...WEEKLY_MODEL_USAGE_BUCKETS.map((bucket) => bucket.widgetType)
80196
80394
  ]);
80197
80395
  var USAGE_DATA_FIELDS = [
80198
80396
  "sessionUsage",
80199
80397
  "sessionResetAt",
80200
80398
  "weeklyUsage",
80201
80399
  "weeklyResetAt",
80202
- "weeklySonnetUsage",
80203
- "weeklySonnetResetAt",
80204
- "weeklyOpusUsage",
80205
- "weeklyOpusResetAt",
80400
+ ...WEEKLY_MODEL_USAGE_BUCKETS.flatMap((bucket) => [bucket.usageField, bucket.resetField]),
80206
80401
  "extraUsageEnabled",
80207
80402
  "extraUsageLimit",
80208
80403
  "extraUsageUsed",
@@ -80213,8 +80408,7 @@ var EMPTY_USAGE_REQUIREMENTS = [];
80213
80408
  var USAGE_WIDGET_REQUIREMENTS = {
80214
80409
  "session-usage": [{ field: "sessionUsage" }],
80215
80410
  "weekly-usage": [{ field: "weeklyUsage" }],
80216
- "weekly-sonnet-usage": [{ field: "weeklySonnetUsage" }],
80217
- "weekly-opus-usage": [{ field: "weeklyOpusUsage" }],
80411
+ ...Object.fromEntries(WEEKLY_MODEL_USAGE_BUCKETS.map((bucket) => [bucket.widgetType, [{ field: bucket.usageField }]])),
80218
80412
  "block-timer": [{ field: "sessionResetAt", suppressFetchError: true }],
80219
80413
  "reset-timer": [{ field: "sessionResetAt", suppressFetchError: true }],
80220
80414
  "weekly-reset-timer": [{ field: "weeklyResetAt", suppressFetchError: true }],
@@ -80235,8 +80429,7 @@ var USAGE_WIDGET_REQUIREMENTS = {
80235
80429
  var USAGE_CURSOR_REQUIREMENTS = {
80236
80430
  "session-usage": { field: "sessionResetAt" },
80237
80431
  "weekly-usage": { field: "weeklyResetAt" },
80238
- "weekly-sonnet-usage": { field: "weeklySonnetResetAt", alternatives: ["weeklyResetAt"] },
80239
- "weekly-opus-usage": { field: "weeklyOpusResetAt", alternatives: ["weeklyResetAt"] }
80432
+ ...Object.fromEntries(WEEKLY_MODEL_USAGE_BUCKETS.map((bucket) => [bucket.widgetType, { field: bucket.resetField, alternatives: ["weeklyResetAt"] }]))
80240
80433
  };
80241
80434
  function hasUsageDependentWidgets(lines) {
80242
80435
  return lines.some((line) => line.some((item) => USAGE_WIDGET_TYPES.has(item.type)));
@@ -80286,21 +80479,14 @@ function hasAnyUsageDataField(data) {
80286
80479
  return USAGE_DATA_FIELDS.some((field) => data?.[field] !== undefined);
80287
80480
  }
80288
80481
  function pickDefinedUsageFields(data) {
80289
- return {
80290
- ...data?.sessionUsage !== undefined ? { sessionUsage: data.sessionUsage } : {},
80291
- ...data?.sessionResetAt !== undefined ? { sessionResetAt: data.sessionResetAt } : {},
80292
- ...data?.weeklyUsage !== undefined ? { weeklyUsage: data.weeklyUsage } : {},
80293
- ...data?.weeklyResetAt !== undefined ? { weeklyResetAt: data.weeklyResetAt } : {},
80294
- ...data?.weeklySonnetUsage !== undefined ? { weeklySonnetUsage: data.weeklySonnetUsage } : {},
80295
- ...data?.weeklySonnetResetAt !== undefined ? { weeklySonnetResetAt: data.weeklySonnetResetAt } : {},
80296
- ...data?.weeklyOpusUsage !== undefined ? { weeklyOpusUsage: data.weeklyOpusUsage } : {},
80297
- ...data?.weeklyOpusResetAt !== undefined ? { weeklyOpusResetAt: data.weeklyOpusResetAt } : {},
80298
- ...data?.extraUsageEnabled !== undefined ? { extraUsageEnabled: data.extraUsageEnabled } : {},
80299
- ...data?.extraUsageLimit !== undefined ? { extraUsageLimit: data.extraUsageLimit } : {},
80300
- ...data?.extraUsageUsed !== undefined ? { extraUsageUsed: data.extraUsageUsed } : {},
80301
- ...data?.extraUsageUtilization !== undefined ? { extraUsageUtilization: data.extraUsageUtilization } : {},
80302
- ...data?.extraUsageCurrency !== undefined ? { extraUsageCurrency: data.extraUsageCurrency } : {}
80303
- };
80482
+ const picked = {};
80483
+ for (const field of USAGE_DATA_FIELDS) {
80484
+ const value = data?.[field];
80485
+ if (value !== undefined) {
80486
+ setUsageField(picked, field, value);
80487
+ }
80488
+ }
80489
+ return picked;
80304
80490
  }
80305
80491
  function mergeUsageData(rateLimitsData, apiData) {
80306
80492
  return {
@@ -80315,28 +80501,28 @@ function epochSecondsToIsoString(epochSeconds) {
80315
80501
  }
80316
80502
  return new Date(epochSeconds * 1000).toISOString();
80317
80503
  }
80504
+ function getRateLimitBucketUsage(bucket) {
80505
+ return bucket?.used_percentage ?? undefined;
80506
+ }
80318
80507
  function extractUsageDataFromRateLimits(rateLimits) {
80319
80508
  if (!rateLimits) {
80320
80509
  return null;
80321
80510
  }
80322
- const sessionUsage = rateLimits.five_hour?.used_percentage ?? undefined;
80323
- const sessionResetAt = epochSecondsToIsoString(rateLimits.five_hour?.resets_at);
80324
- const weeklyUsage = rateLimits.seven_day?.used_percentage ?? undefined;
80325
- const weeklyResetAt = epochSecondsToIsoString(rateLimits.seven_day?.resets_at);
80326
- const weeklySonnetUsage = rateLimits.seven_day_sonnet === null ? 0 : rateLimits.seven_day_sonnet?.used_percentage ?? undefined;
80327
- const weeklySonnetResetAt = epochSecondsToIsoString(rateLimits.seven_day_sonnet?.resets_at);
80328
- const weeklyOpusUsage = rateLimits.seven_day_opus === null ? 0 : rateLimits.seven_day_opus?.used_percentage ?? undefined;
80329
- const weeklyOpusResetAt = epochSecondsToIsoString(rateLimits.seven_day_opus?.resets_at);
80511
+ const rateLimitBuckets = rateLimits;
80330
80512
  const usageData = {
80331
- sessionUsage,
80332
- sessionResetAt,
80333
- weeklyUsage,
80334
- weeklyResetAt,
80335
- weeklySonnetUsage,
80336
- weeklySonnetResetAt,
80337
- weeklyOpusUsage,
80338
- weeklyOpusResetAt
80513
+ sessionUsage: rateLimits.five_hour?.used_percentage ?? undefined,
80514
+ sessionResetAt: epochSecondsToIsoString(rateLimits.five_hour?.resets_at),
80515
+ weeklyUsage: rateLimits.seven_day?.used_percentage ?? undefined,
80516
+ weeklyResetAt: epochSecondsToIsoString(rateLimits.seven_day?.resets_at)
80339
80517
  };
80518
+ for (const bucket of WEEKLY_MODEL_USAGE_BUCKETS) {
80519
+ if (!bucket.apiBucketKey) {
80520
+ continue;
80521
+ }
80522
+ const rateLimitBucket = rateLimitBuckets[bucket.apiBucketKey];
80523
+ setUsageField(usageData, bucket.usageField, getRateLimitBucketUsage(rateLimitBucket));
80524
+ setUsageField(usageData, bucket.resetField, epochSecondsToIsoString(rateLimitBucket?.resets_at));
80525
+ }
80340
80526
  return hasAnyUsageDataField(usageData) ? usageData : null;
80341
80527
  }
80342
80528
  async function prefetchUsageDataIfNeeded(lines, data) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline",
3
- "version": "2.2.25",
3
+ "version": "2.2.26",
4
4
  "bugs": {
5
5
  "url": "https://github.com/sirmalloc/ccstatusline/issues"
6
6
  },