ccstatusline-usage 2.3.2 → 2.3.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/README.md CHANGED
@@ -66,6 +66,16 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
66
66
 
67
67
  ## 🆕 Recent Updates
68
68
 
69
+ ### [v2.3.4](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.4) - Upstream sync + extra usage fix
70
+
71
+ - [sirmalloc/ccstatusline](https://github.com/sirmalloc/ccstatusline): **Native rate_limits support** — Upstream now uses Claude Code 2.1.80's native `rate_limits` field for usage data
72
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Extra usage regression fix** — Reset timer widget now supplements native `rate_limits` data with API fetch for extra usage fields that upstream's pipeline omits
73
+
74
+ ### [v2.3.3](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.3) - CI pipeline fix
75
+
76
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **CI pipeline fix** — Fixed TypeScript errors (model union type narrowing, missing `extraUsageBalance` setting), disabled broken `import/order` ESLint rule (incompatible with ESLint 10), resolved all remaining lint errors in fork-specific files
77
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Release workflow** — Added mandatory CI pipeline check before tagging/releasing
78
+
69
79
  ### [v2.3.2](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.2) - Off Peak widget + WeeklyPace compact fix
70
80
 
71
81
  - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Off Peak widget** — New widget showing `Off peak: Off-peak 2x` or `Off peak: Peak` during the Anthropic March 2026 Spring Break promotion (off-peak = weekdays outside 8 AM–2 PM ET, weekends all day). Widget automatically hides after March 28, 2026 23:59 PT.
@@ -52452,6 +52452,7 @@ var init_Settings = __esm(() => {
52452
52452
  theme: undefined,
52453
52453
  autoAlign: false
52454
52454
  }),
52455
+ extraUsageBalance: exports_external.number().optional(),
52455
52456
  updatemessage: exports_external.object({
52456
52457
  message: exports_external.string().nullable().optional(),
52457
52458
  remaining: exports_external.number().nullable().optional()
@@ -54071,7 +54072,7 @@ function getTerminalWidth() {
54071
54072
  function canDetectTerminalWidth() {
54072
54073
  return probeTerminalWidth() !== null;
54073
54074
  }
54074
- var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.2";
54075
+ var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.4";
54075
54076
  var init_terminal = () => {};
54076
54077
 
54077
54078
  // src/utils/renderer.ts
@@ -61431,7 +61432,7 @@ import * as path7 from "path";
61431
61432
  function readTokenFromFile() {
61432
61433
  try {
61433
61434
  const creds = JSON.parse(fs9.readFileSync(CRED_FILE, "utf8"));
61434
- return creds?.claudeAiOauth?.accessToken ?? null;
61435
+ return creds.claudeAiOauth?.accessToken ?? null;
61435
61436
  } catch {
61436
61437
  return null;
61437
61438
  }
@@ -61440,7 +61441,7 @@ function readTokenFromKeychain() {
61440
61441
  try {
61441
61442
  const result2 = execSync5('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
61442
61443
  const parsed = JSON.parse(result2);
61443
- return parsed?.claudeAiOauth?.accessToken ?? null;
61444
+ return parsed.claudeAiOauth?.accessToken ?? null;
61444
61445
  } catch {
61445
61446
  return null;
61446
61447
  }
@@ -61457,8 +61458,7 @@ function getToken() {
61457
61458
  let token = null;
61458
61459
  if (process.platform === "darwin")
61459
61460
  token = readTokenFromKeychain();
61460
- if (!token)
61461
- token = readTokenFromFile();
61461
+ token ??= readTokenFromFile();
61462
61462
  if (token) {
61463
61463
  cachedToken = token;
61464
61464
  tokenCacheTime = now2;
@@ -61745,7 +61745,8 @@ class ResetTimerWidget {
61745
61745
  const data = fetchApiData();
61746
61746
  if (data.error)
61747
61747
  return getErrorMessage(data.error);
61748
- const modelId = context.data?.model?.id ?? "";
61748
+ const model = context.data?.model;
61749
+ const modelId = (typeof model === "string" ? model : model?.id) ?? "";
61749
61750
  const is1mModel = modelId.includes("[1m]");
61750
61751
  const isOpus = modelId.includes("opus");
61751
61752
  const isChargedModel = is1mModel && !isOpus;
@@ -62614,6 +62615,26 @@ function isOffPeak(now2) {
62614
62615
  const isPeak = utcHour >= PEAK_START_UTC_HOUR && utcHour < PEAK_END_UTC_HOUR;
62615
62616
  return !isPeak;
62616
62617
  }
62618
+ function minutesUntilFlip(now2) {
62619
+ const dayOfWeek = now2.getUTCDay();
62620
+ const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
62621
+ if (isWeekend)
62622
+ return null;
62623
+ const utcHour = now2.getUTCHours();
62624
+ const isPeak = utcHour >= PEAK_START_UTC_HOUR && utcHour < PEAK_END_UTC_HOUR;
62625
+ const flipHour = isPeak ? PEAK_END_UTC_HOUR : PEAK_START_UTC_HOUR;
62626
+ const target = new Date(now2);
62627
+ if (!isPeak && utcHour >= PEAK_END_UTC_HOUR) {
62628
+ target.setUTCDate(now2.getUTCDate() + 1);
62629
+ }
62630
+ target.setUTCHours(flipHour, 0, 0, 0);
62631
+ return Math.max(0, Math.round((target.getTime() - now2.getTime()) / 60000));
62632
+ }
62633
+ function formatCountdown(minutes) {
62634
+ const h = Math.floor(minutes / 60);
62635
+ const m = minutes % 60;
62636
+ return `${h}:${String(m).padStart(2, "0")}`;
62637
+ }
62617
62638
 
62618
62639
  class OffPeakWidget {
62619
62640
  getDefaultColor() {
@@ -62633,16 +62654,19 @@ class OffPeakWidget {
62633
62654
  }
62634
62655
  render(item, context, _settings) {
62635
62656
  if (context.isPreview) {
62636
- return item.rawValue ? "Off-peak 2x" : "Off peak: Off-peak 2x";
62657
+ return item.rawValue ? "Off-peak 2x (3:42)" : "Off-peak 2x (3:42)";
62637
62658
  }
62638
- const result2 = isOffPeak(new Date);
62639
- if (result2 === null)
62659
+ const now2 = new Date;
62660
+ const offPeak = isOffPeak(now2);
62661
+ if (offPeak === null)
62640
62662
  return null;
62641
- const value = result2 ? "Off-peak 2x" : "Peak";
62663
+ const mins = minutesUntilFlip(now2);
62664
+ const countdown = mins !== null ? ` (${formatCountdown(mins)} hr)` : "";
62642
62665
  const mobile = (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < 80;
62643
- if (item.rawValue || mobile)
62644
- return value;
62645
- return `Off peak: ${value}`;
62666
+ if (offPeak) {
62667
+ return mobile ? `2x${countdown}` : `Off-peak 2x${countdown}`;
62668
+ }
62669
+ return `Peak${countdown}`;
62646
62670
  }
62647
62671
  supportsRawValue() {
62648
62672
  return true;
@@ -68973,6 +68997,10 @@ var CoercedNumberSchema = exports_external.preprocess((value) => {
68973
68997
  const parsed = Number(trimmed);
68974
68998
  return Number.isFinite(parsed) ? parsed : value;
68975
68999
  }, exports_external.number());
69000
+ var RateLimitPeriodSchema = exports_external.object({
69001
+ used_percentage: CoercedNumberSchema.nullable().optional(),
69002
+ resets_at: CoercedNumberSchema.nullable().optional()
69003
+ });
68976
69004
  var StatusJSONSchema = exports_external.looseObject({
68977
69005
  hook_event_name: exports_external.string().optional(),
68978
69006
  session_id: exports_external.string().optional(),
@@ -69015,7 +69043,11 @@ var StatusJSONSchema = exports_external.looseObject({
69015
69043
  used_percentage: CoercedNumberSchema.nullable().optional(),
69016
69044
  remaining_percentage: CoercedNumberSchema.nullable().optional()
69017
69045
  }).nullable().optional(),
69018
- vim: exports_external.object({ mode: exports_external.string().optional() }).nullable().optional()
69046
+ vim: exports_external.object({ mode: exports_external.string().optional() }).nullable().optional(),
69047
+ rate_limits: exports_external.object({
69048
+ five_hour: RateLimitPeriodSchema.optional(),
69049
+ seven_day: RateLimitPeriodSchema.optional()
69050
+ }).nullable().optional()
69019
69051
  });
69020
69052
 
69021
69053
  // src/ccstatusline.ts
@@ -69148,11 +69180,46 @@ var USAGE_WIDGET_TYPES = new Set([
69148
69180
  function hasUsageDependentWidgets(lines) {
69149
69181
  return lines.some((line) => line.some((item) => USAGE_WIDGET_TYPES.has(item.type)));
69150
69182
  }
69151
- async function prefetchUsageDataIfNeeded(lines) {
69183
+ function epochSecondsToIsoString(epochSeconds) {
69184
+ if (epochSeconds === null || epochSeconds === undefined || !Number.isFinite(epochSeconds)) {
69185
+ return;
69186
+ }
69187
+ return new Date(epochSeconds * 1000).toISOString();
69188
+ }
69189
+ function extractUsageDataFromRateLimits(rateLimits) {
69190
+ if (!rateLimits) {
69191
+ return null;
69192
+ }
69193
+ const sessionUsage = rateLimits.five_hour?.used_percentage ?? undefined;
69194
+ const sessionResetAt = epochSecondsToIsoString(rateLimits.five_hour?.resets_at);
69195
+ const weeklyUsage = rateLimits.seven_day?.used_percentage ?? undefined;
69196
+ const weeklyResetAt = epochSecondsToIsoString(rateLimits.seven_day?.resets_at);
69197
+ if (sessionUsage === undefined && weeklyUsage === undefined) {
69198
+ return null;
69199
+ }
69200
+ return { sessionUsage, sessionResetAt, weeklyUsage, weeklyResetAt };
69201
+ }
69202
+ function hasCompleteRateLimitsUsageData(usageData) {
69203
+ return usageData?.sessionUsage !== undefined && usageData.sessionResetAt !== undefined && usageData.weeklyUsage !== undefined && usageData.weeklyResetAt !== undefined;
69204
+ }
69205
+ function hasExtraUsageDependentWidgets(lines) {
69206
+ return lines.some((line) => line.some((item) => item.type === "reset-timer"));
69207
+ }
69208
+ async function prefetchUsageDataIfNeeded(lines, data) {
69152
69209
  if (!hasUsageDependentWidgets(lines)) {
69153
69210
  return null;
69154
69211
  }
69155
- return await fetchUsageData();
69212
+ const rateLimitsData = extractUsageDataFromRateLimits(data?.rate_limits);
69213
+ if (hasCompleteRateLimitsUsageData(rateLimitsData)) {
69214
+ if (hasExtraUsageDependentWidgets(lines)) {
69215
+ const apiData = await fetchUsageData();
69216
+ if (apiData.error === undefined) {
69217
+ return { ...rateLimitsData, extraUsageEnabled: apiData.extraUsageEnabled, extraUsageLimit: apiData.extraUsageLimit, extraUsageUsed: apiData.extraUsageUsed, extraUsageUtilization: apiData.extraUsageUtilization };
69218
+ }
69219
+ }
69220
+ return rateLimitsData;
69221
+ }
69222
+ return fetchUsageData();
69156
69223
  }
69157
69224
 
69158
69225
  // src/ccstatusline.ts
@@ -69224,7 +69291,7 @@ async function renderMultipleLines(data) {
69224
69291
  if (hasSessionClock && !hasSessionDurationInStatusJson(data) && data.transcript_path) {
69225
69292
  sessionDuration = await getSessionDuration(data.transcript_path);
69226
69293
  }
69227
- const usageData = await prefetchUsageDataIfNeeded(lines);
69294
+ const usageData = await prefetchUsageDataIfNeeded(lines, data);
69228
69295
  let speedMetrics = null;
69229
69296
  let windowedSpeedMetrics = null;
69230
69297
  if (hasSpeedItems && data.transcript_path) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline-usage",
3
- "version": "2.3.2",
3
+ "version": "2.3.4",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",