ccstatusline-usage 2.0.39 → 2.0.40

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.
Files changed (2) hide show
  1. package/dist/ccstatusline.js +119 -42
  2. package/package.json +1 -1
@@ -51450,7 +51450,7 @@ import { execSync as execSync3 } from "child_process";
51450
51450
  import * as fs5 from "fs";
51451
51451
  import * as path4 from "path";
51452
51452
  var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils";
51453
- var PACKAGE_VERSION = "2.0.39";
51453
+ var PACKAGE_VERSION = "2.0.40";
51454
51454
  function getPackageVersion() {
51455
51455
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
51456
51456
  return PACKAGE_VERSION;
@@ -54487,49 +54487,99 @@ class ClaudeSessionIdWidget {
54487
54487
  }
54488
54488
  }
54489
54489
  // src/widgets/ApiUsage.tsx
54490
- import { execSync as execSync8, spawnSync } from "child_process";
54490
+ import {
54491
+ execSync as execSync8,
54492
+ spawnSync
54493
+ } from "child_process";
54491
54494
  import * as fs7 from "fs";
54492
54495
  import * as path6 from "path";
54493
54496
  var CACHE_FILE = path6.join(process.env.HOME ?? "", ".cache", "ccstatusline-api.json");
54497
+ var LOCK_FILE = path6.join(process.env.HOME ?? "", ".cache", "ccstatusline-api.lock");
54494
54498
  var CACHE_MAX_AGE = 180;
54499
+ var LOCK_MAX_AGE = 30;
54500
+ var TOKEN_CACHE_MAX_AGE = 3600;
54495
54501
  var cachedData = null;
54496
54502
  var cacheTime = 0;
54503
+ var cachedToken = null;
54504
+ var tokenCacheTime = 0;
54497
54505
  function getToken() {
54506
+ const now = Math.floor(Date.now() / 1000);
54507
+ if (cachedToken && now - tokenCacheTime < TOKEN_CACHE_MAX_AGE) {
54508
+ return cachedToken;
54509
+ }
54498
54510
  try {
54499
54511
  const isMac = process.platform === "darwin";
54500
54512
  if (isMac) {
54501
54513
  const result = execSync8('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
54502
54514
  const parsed = JSON.parse(result);
54503
- return parsed?.claudeAiOauth?.accessToken ?? null;
54515
+ const token = parsed?.claudeAiOauth?.accessToken ?? null;
54516
+ if (token) {
54517
+ cachedToken = token;
54518
+ tokenCacheTime = now;
54519
+ }
54520
+ return token;
54504
54521
  } else {
54505
54522
  const credFile = path6.join(process.env.HOME ?? "", ".claude", ".credentials.json");
54506
- if (fs7.existsSync(credFile)) {
54507
- const creds = JSON.parse(fs7.readFileSync(credFile, "utf8"));
54508
- return creds?.claudeAiOauth?.accessToken ?? null;
54523
+ const creds = JSON.parse(fs7.readFileSync(credFile, "utf8"));
54524
+ const token = creds?.claudeAiOauth?.accessToken ?? null;
54525
+ if (token) {
54526
+ cachedToken = token;
54527
+ tokenCacheTime = now;
54509
54528
  }
54529
+ return token;
54510
54530
  }
54511
- } catch {}
54512
- return null;
54531
+ } catch {
54532
+ return null;
54533
+ }
54534
+ }
54535
+ function readStaleCache() {
54536
+ try {
54537
+ return JSON.parse(fs7.readFileSync(CACHE_FILE, "utf8"));
54538
+ } catch {
54539
+ return null;
54540
+ }
54513
54541
  }
54514
54542
  function fetchApiData() {
54515
54543
  const now = Math.floor(Date.now() / 1000);
54516
- if (cachedData && now - cacheTime < CACHE_MAX_AGE) {
54544
+ if (cachedData && !cachedData.error && now - cacheTime < CACHE_MAX_AGE) {
54517
54545
  return cachedData;
54518
54546
  }
54519
54547
  try {
54520
- if (fs7.existsSync(CACHE_FILE)) {
54521
- const stat = fs7.statSync(CACHE_FILE);
54522
- const fileAge = now - Math.floor(stat.mtimeMs / 1000);
54523
- if (fileAge < CACHE_MAX_AGE) {
54524
- cachedData = JSON.parse(fs7.readFileSync(CACHE_FILE, "utf8"));
54548
+ const stat = fs7.statSync(CACHE_FILE);
54549
+ const fileAge = now - Math.floor(stat.mtimeMs / 1000);
54550
+ if (fileAge < CACHE_MAX_AGE) {
54551
+ const fileData = JSON.parse(fs7.readFileSync(CACHE_FILE, "utf8"));
54552
+ if (!fileData.error) {
54553
+ cachedData = fileData;
54525
54554
  cacheTime = now;
54526
- return cachedData;
54555
+ return fileData;
54527
54556
  }
54528
54557
  }
54529
54558
  } catch {}
54559
+ try {
54560
+ const lockStat = fs7.statSync(LOCK_FILE);
54561
+ const lockAge = now - Math.floor(lockStat.mtimeMs / 1000);
54562
+ if (lockAge < LOCK_MAX_AGE) {
54563
+ const stale = readStaleCache();
54564
+ if (stale && !stale.error)
54565
+ return stale;
54566
+ return { error: "timeout" };
54567
+ }
54568
+ } catch {}
54569
+ try {
54570
+ const lockDir = path6.dirname(LOCK_FILE);
54571
+ if (!fs7.existsSync(lockDir)) {
54572
+ fs7.mkdirSync(lockDir, { recursive: true });
54573
+ }
54574
+ fs7.writeFileSync(LOCK_FILE, "");
54575
+ } catch {}
54530
54576
  const token = getToken();
54531
- if (!token)
54532
- return null;
54577
+ if (!token) {
54578
+ const stale = readStaleCache();
54579
+ if (stale && !stale.error)
54580
+ return stale;
54581
+ return { error: "no-credentials" };
54582
+ }
54533
54583
  try {
54534
54584
  const curlResult = spawnSync("curl", [
54535
54585
  "-s",
@@ -54540,12 +54590,15 @@ function fetchApiData() {
54540
54590
  `Authorization: Bearer ${token}`,
54541
54591
  "-H",
54542
54592
  "anthropic-beta: oauth-2025-04-20"
54543
- ], { encoding: "utf8", timeout: 5000 });
54544
- if (curlResult.error || curlResult.status !== 0)
54545
- return null;
54546
- const result = curlResult.stdout;
54547
- const data = JSON.parse(result);
54548
- let apiData = {};
54593
+ ], { encoding: "utf8", timeout: 6000 });
54594
+ if (curlResult.error || curlResult.status !== 0 || !curlResult.stdout) {
54595
+ const stale = readStaleCache();
54596
+ if (stale && !stale.error)
54597
+ return stale;
54598
+ return { error: "api-error" };
54599
+ }
54600
+ const data = JSON.parse(curlResult.stdout);
54601
+ const apiData = {};
54549
54602
  if (data.five_hour) {
54550
54603
  apiData.sessionUsage = data.five_hour.utilization;
54551
54604
  apiData.sessionResetAt = data.five_hour.reset_at;
@@ -54553,6 +54606,12 @@ function fetchApiData() {
54553
54606
  if (data.seven_day) {
54554
54607
  apiData.weeklyUsage = data.seven_day.utilization;
54555
54608
  }
54609
+ if (apiData.sessionUsage === undefined && apiData.weeklyUsage === undefined) {
54610
+ const stale = readStaleCache();
54611
+ if (stale && !stale.error)
54612
+ return stale;
54613
+ return { error: "parse-error" };
54614
+ }
54556
54615
  try {
54557
54616
  const cacheDir = path6.dirname(CACHE_FILE);
54558
54617
  if (!fs7.existsSync(cacheDir)) {
@@ -54564,7 +54623,22 @@ function fetchApiData() {
54564
54623
  cacheTime = now;
54565
54624
  return apiData;
54566
54625
  } catch {
54567
- return null;
54626
+ const stale = readStaleCache();
54627
+ if (stale && !stale.error)
54628
+ return stale;
54629
+ return { error: "parse-error" };
54630
+ }
54631
+ }
54632
+ function getErrorMessage(error43) {
54633
+ switch (error43) {
54634
+ case "no-credentials":
54635
+ return "[No credentials]";
54636
+ case "timeout":
54637
+ return "[Timeout]";
54638
+ case "api-error":
54639
+ return "[API Error]";
54640
+ case "parse-error":
54641
+ return "[Parse Error]";
54568
54642
  }
54569
54643
  }
54570
54644
  function makeProgressBar(percent, width = 15) {
@@ -54583,23 +54657,24 @@ class SessionUsageWidget {
54583
54657
  getDisplayName() {
54584
54658
  return "Session Usage";
54585
54659
  }
54586
- getEditorDisplay() {
54660
+ getEditorDisplay(item) {
54587
54661
  return { displayText: this.getDisplayName() };
54588
54662
  }
54589
- render(item, context) {
54663
+ render(item, context, settings) {
54590
54664
  if (context.isPreview)
54591
54665
  return "Session: [███░░░░░░░░░░░░] 20%";
54592
54666
  const data = fetchApiData();
54593
- if (!data || data.sessionUsage === undefined) {
54667
+ if (data.error)
54668
+ return getErrorMessage(data.error);
54669
+ if (data.sessionUsage === undefined)
54594
54670
  return null;
54595
- }
54596
54671
  const percent = data.sessionUsage;
54597
54672
  return `Session: ${makeProgressBar(percent)} ${percent.toFixed(1)}%`;
54598
54673
  }
54599
54674
  supportsRawValue() {
54600
54675
  return false;
54601
54676
  }
54602
- supportsColors() {
54677
+ supportsColors(item) {
54603
54678
  return true;
54604
54679
  }
54605
54680
  }
@@ -54614,23 +54689,24 @@ class WeeklyUsageWidget {
54614
54689
  getDisplayName() {
54615
54690
  return "Weekly Usage";
54616
54691
  }
54617
- getEditorDisplay() {
54692
+ getEditorDisplay(item) {
54618
54693
  return { displayText: this.getDisplayName() };
54619
54694
  }
54620
- render(item, context) {
54695
+ render(item, context, settings) {
54621
54696
  if (context.isPreview)
54622
54697
  return "Weekly: [██░░░░░░░░░░░░░] 12%";
54623
54698
  const data = fetchApiData();
54624
- if (!data || data.weeklyUsage === undefined) {
54699
+ if (data.error)
54700
+ return getErrorMessage(data.error);
54701
+ if (data.weeklyUsage === undefined)
54625
54702
  return null;
54626
- }
54627
54703
  const percent = data.weeklyUsage;
54628
54704
  return `Weekly: ${makeProgressBar(percent)} ${percent.toFixed(1)}%`;
54629
54705
  }
54630
54706
  supportsRawValue() {
54631
54707
  return false;
54632
54708
  }
54633
- supportsColors() {
54709
+ supportsColors(item) {
54634
54710
  return true;
54635
54711
  }
54636
54712
  }
@@ -54645,16 +54721,17 @@ class ResetTimerWidget {
54645
54721
  getDisplayName() {
54646
54722
  return "Reset Timer";
54647
54723
  }
54648
- getEditorDisplay() {
54724
+ getEditorDisplay(item) {
54649
54725
  return { displayText: this.getDisplayName() };
54650
54726
  }
54651
- render(item, context) {
54727
+ render(item, context, settings) {
54652
54728
  if (context.isPreview)
54653
54729
  return "4:30 hr";
54654
54730
  const data = fetchApiData();
54655
- if (!data || !data.sessionResetAt) {
54731
+ if (data.error)
54732
+ return getErrorMessage(data.error);
54733
+ if (!data.sessionResetAt)
54656
54734
  return null;
54657
- }
54658
54735
  try {
54659
54736
  const resetTime = new Date(data.sessionResetAt).getTime();
54660
54737
  const now = Date.now();
@@ -54671,7 +54748,7 @@ class ResetTimerWidget {
54671
54748
  supportsRawValue() {
54672
54749
  return false;
54673
54750
  }
54674
- supportsColors() {
54751
+ supportsColors(item) {
54675
54752
  return true;
54676
54753
  }
54677
54754
  }
@@ -54686,10 +54763,10 @@ class ContextBarWidget {
54686
54763
  getDisplayName() {
54687
54764
  return "Context Bar";
54688
54765
  }
54689
- getEditorDisplay() {
54766
+ getEditorDisplay(item) {
54690
54767
  return { displayText: this.getDisplayName() };
54691
54768
  }
54692
- render(item, context) {
54769
+ render(item, context, settings) {
54693
54770
  if (context.isPreview)
54694
54771
  return "Context: [████░░░░░░░░░░░] 50k/200k (25%)";
54695
54772
  const cw = context.data?.context_window;
@@ -54713,7 +54790,7 @@ class ContextBarWidget {
54713
54790
  supportsRawValue() {
54714
54791
  return false;
54715
54792
  }
54716
- supportsColors() {
54793
+ supportsColors(item) {
54717
54794
  return true;
54718
54795
  }
54719
54796
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline-usage",
3
- "version": "2.0.39",
3
+ "version": "2.0.40",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",