tokenmaxxing 0.1.0__tar.gz → 0.1.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tokenmaxxing
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Menu bar app showing your live Claude Code session and weekly usage as a colored progress bar.
5
5
  Project-URL: Homepage, https://github.com/alvations/tokenmaxxing
6
6
  Project-URL: Repository, https://github.com/alvations/tokenmaxxing
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tokenmaxxing"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "Menu bar app showing your live Claude Code session and weekly usage as a colored progress bar."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,6 +1,6 @@
1
1
  """tokenmaxxing — menu bar app for Claude Code session and weekly usage."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.2"
4
4
 
5
5
  from tokenmaxxing.app import main
6
6
 
@@ -61,6 +61,8 @@ LOADING_TEXT = "loading…"
61
61
  TIMER_INTERVAL = 1
62
62
 
63
63
  REFRESH_SECONDS = 300 # 5 min: OAuth usage endpoint has aggressive undocumented rate limits
64
+ STALE_AFTER_SECONDS = 600 # 10 min: only mark cached data "(stale)" past this age — one
65
+ # missed poll cycle (~5 min) shouldn't trigger the warning
64
66
  REFRESH_MAX_BACKOFF = 1800 # Max backoff: 30 minutes
65
67
  BACKOFF_EXP_CAP = 11 # 2^11 = 2048s, lets REFRESH_MAX_BACKOFF (1800s) become the binding cap
66
68
  HTTP_TIMEOUT = 10
@@ -623,12 +625,20 @@ class ClaudeMonitorApp(rumps.App):
623
625
  consecutive_failures = 0
624
626
  self._backoff_until = 0
625
627
  self._record_snapshot(payload)
628
+ sleep_for = REFRESH_SECONDS
626
629
  elif is_rate_limited:
627
630
  consecutive_failures += 1
628
631
  backoff = _calc_backoff(consecutive_failures)
629
632
  self._backoff_until = time.time() + backoff
633
+ # Retry as soon as the backoff window allows, instead of waiting
634
+ # the full REFRESH_SECONDS — otherwise the menu keeps showing
635
+ # "[rate limited]" for ~5 min after a single transient 429 even
636
+ # when the API recovered within seconds.
637
+ sleep_for = backoff
638
+ else:
639
+ sleep_for = REFRESH_SECONDS
630
640
 
631
- self._wake.wait(timeout=REFRESH_SECONDS)
641
+ self._wake.wait(timeout=sleep_for)
632
642
  self._wake.clear()
633
643
 
634
644
  def _apply_pending(self, _sender):
@@ -799,13 +809,24 @@ class ClaudeMonitorApp(rumps.App):
799
809
  return EXTRA_USAGE_LABEL + (SEPARATOR.join(parts) or EXTRA_USAGE_ENABLED)
800
810
 
801
811
  def _get_status_suffix(self) -> str:
802
- """Return status suffix: ' [rate limited]', ' (stale)', or ''."""
812
+ """Return status suffix: ' [rate limited]', ' (stale)', or ''.
813
+
814
+ '(stale)' only fires when cached data is older than STALE_AFTER_SECONDS,
815
+ not on every transient error — a single failed poll shouldn't make
816
+ ~5-min-old data look untrusted.
817
+ """
803
818
  if self._is_rate_limited:
804
819
  return STATUS_RATE_LIMITED
805
- if self._latest_error:
820
+ if self._latest_error and self._is_data_old():
806
821
  return STATUS_STALE
807
822
  return ""
808
823
 
824
+ def _is_data_old(self) -> bool:
825
+ if self._latest_at is None:
826
+ return True # never had a successful poll
827
+ age = (_utc_now() - self._latest_at).total_seconds()
828
+ return age > STALE_AFTER_SECONDS
829
+
809
830
  def _view_is_available(self, view) -> bool:
810
831
  """Check if a view dict has valid utilization data."""
811
832
  return isinstance(view, dict) and view.get("utilization") is not None
File without changes
File without changes
File without changes