tokenmaxxing 0.2.1__tar.gz → 0.2.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.2.1
3
+ Version: 0.2.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.2.1"
7
+ version = "0.2.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.2.1"
3
+ __version__ = "0.2.2"
4
4
 
5
5
  from tokenmaxxing.app import main
6
6
 
@@ -78,8 +78,6 @@ REFRESH_SECONDS = 1800 # 30 min: /api/oauth/usage tolerates ~hourly polling but
78
78
  # at ~5 min cadence with a sticky multi-hour cooldown. 30 min keeps
79
79
  # us well below the throttle threshold; on-demand menu opens still
80
80
  # refresh instantly.
81
- SUSTAINED_429_THRESHOLD = 3 # after this many consecutive 429s, fall back to /v1/messages
82
- # headers (which cost ~10 tokens/poll) until /usage recovers
83
81
  STALE_AFTER_SECONDS = 600 # 10 min: only mark cached data "(stale)" past this age — one
84
82
  # missed poll cycle (~5 min) shouldn't trigger the warning
85
83
  REFRESH_MAX_BACKOFF = 1800 # Max backoff: 30 minutes
@@ -724,6 +722,7 @@ class ClaudeMonitorApp(rumps.App):
724
722
  self._pending_lock = threading.Lock()
725
723
  self._wake = threading.Event()
726
724
  self._backoff_until = 0 # unix timestamp; worker won't poll until after this
725
+ self._force_fallback_next = False # set by "Force fresh" menu; consumed by next poll
727
726
  self._history_lock = threading.Lock()
728
727
  self._history = _load_history()
729
728
 
@@ -773,6 +772,8 @@ class ClaudeMonitorApp(rumps.App):
773
772
  self.stats_item = rumps.MenuItem(HISTORY_COLLECTING)
774
773
  self._dashboard_item = rumps.MenuItem("Open Claude dashboard…", callback=self._open_dashboard)
775
774
  self._refresh_item = rumps.MenuItem("Refresh now", callback=self._manual_refresh)
775
+ self._refresh_paid_item = rumps.MenuItem(
776
+ "Force fresh (uses ~10 tokens)", callback=self._manual_refresh_paid)
776
777
 
777
778
  self.menu = self._build_menu_items()
778
779
 
@@ -800,12 +801,14 @@ class ClaudeMonitorApp(rumps.App):
800
801
  continue
801
802
 
802
803
  oauth_data = _get_oauth_data()
803
- # After SUSTAINED_429_THRESHOLD consecutive /api/oauth/usage 429s,
804
- # unstick the menu by falling back to the paid /v1/messages
805
- # header path. We don't enable the fallback by default because it
806
- # costs ~10 tokens/poll we only pay that when the free endpoint
807
- # is genuinely stuck in a multi-hour cooldown.
808
- allow_fallback = consecutive_failures >= SUSTAINED_429_THRESHOLD
804
+ # /v1/messages fallback is opt-in only — the user explicitly
805
+ # triggers it via the "Force fresh (uses ~10 tokens)" menu item,
806
+ # which sets _force_fallback_next for exactly one poll. The
807
+ # default path stays zero-token even when /api/oauth/usage is
808
+ # stuck we'd rather show stale data than silently bill the
809
+ # user's quota.
810
+ allow_fallback = self._force_fallback_next
811
+ self._force_fallback_next = False
809
812
  payload, err, is_rate_limited, retry_after = fetch_usage(
810
813
  oauth_data, allow_messages_fallback=allow_fallback)
811
814
  # Drop any Refresh-now clicks that arrived during the poll — the
@@ -882,6 +885,17 @@ class ClaudeMonitorApp(rumps.App):
882
885
  # Without this, when the worker is in backoff (e.g. after sustained
883
886
  # 429s), the wake event fires but the loop top-checks _backoff_until
884
887
  # and goes right back to waiting — the click is swallowed.
888
+ # Zero token cost: hits /api/oauth/usage only.
889
+ self._backoff_until = 0
890
+ self._wake.set()
891
+
892
+ def _manual_refresh_paid(self, _sender):
893
+ # Opt-in escape hatch when /api/oauth/usage is stuck in a long
894
+ # cooldown. Costs ~10 tokens for one /v1/messages probe whose
895
+ # response headers carry the current rate-limit values. Consumed
896
+ # exactly once by the next poll; subsequent polls revert to the
897
+ # free path.
898
+ self._force_fallback_next = True
885
899
  self._backoff_until = 0
886
900
  self._wake.set()
887
901
 
@@ -962,6 +976,7 @@ class ClaudeMonitorApp(rumps.App):
962
976
  None,
963
977
  self.last_update_item,
964
978
  self._refresh_item,
979
+ self._refresh_paid_item,
965
980
  ])
966
981
  return items
967
982
 
File without changes
File without changes
File without changes