usage-cli 0.34.2__py3-none-any.whl → 0.34.4__py3-none-any.whl

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.
@@ -76,7 +76,8 @@ def validate_muse_cache(payload: object) -> MuseQuotaResult | None:
76
76
  if not isinstance(window, dict):
77
77
  return None
78
78
  used, reset = window.get("used_percent"), window.get("resets_at")
79
- if not _number(used) or not _number(reset) or not 0 <= used <= 100:
79
+ # An exhausted window reports past 100 (e.g. 104); the row clamps it for display.
80
+ if not _number(used) or not _number(reset) or used < 0:
80
81
  return None
81
82
  windows.append(MuseQuotaWindow(used_percent=float(used), resets_at=float(reset)))
82
83
  return MuseQuotaResult(
@@ -171,6 +171,8 @@ def _run_codex_ping(codex_bin: str) -> subprocess.CompletedProcess[str]:
171
171
  "-m",
172
172
  "gpt-5.6-luna",
173
173
  "--skip-git-repo-check",
174
+ # Keep the ping out of the user's `codex resume` list.
175
+ "--ephemeral",
174
176
  "-c",
175
177
  "features.hooks=false",
176
178
  "-c",
quota/window_keeper.py CHANGED
@@ -172,6 +172,12 @@ def _run_claude_ping(claude_bin: str) -> subprocess.CompletedProcess[str]:
172
172
  "haiku",
173
173
  "--settings",
174
174
  str(KEEPER_CLAUDE_SETTINGS_PATH),
175
+ # No tools or MCP servers keeps the ping small; no persistence keeps
176
+ # it out of the user's /resume list.
177
+ "--tools",
178
+ "",
179
+ "--strict-mcp-config",
180
+ "--no-session-persistence",
175
181
  ],
176
182
  stdin=subprocess.DEVNULL,
177
183
  capture_output=True,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: usage-cli
3
- Version: 0.34.2
3
+ Version: 0.34.4
4
4
  Summary: usage — 在 macOS menu bar 與 Windows 系統匣顯示 Claude Code、Codex、Antigravity 用量的小工具(也提供終端機 TUI)
5
5
  License-Expression: AGPL-3.0-only
6
6
  Requires-Python: >=3.13
@@ -53,7 +53,7 @@ loaders/history_loader.py,sha256=jZqfXzmAENoVXl86vAFkBmn8iwlSWMMuvUWJUt2Yl10,173
53
53
  loaders/jsonl_limits.py,sha256=76j9ikF4Rd-zGSFHubmpV32eUQ6zhtHsPp9l-zo9dmo,543
54
54
  loaders/jsonl_utils.py,sha256=fWMDCP2skgNvdERLevxaQtcEB7nFzgSG_vxjPJpB0ug,1118
55
55
  loaders/muse_loader.py,sha256=F5Lg9zPuBNZ2y4pm9EYHWiNS1WTonp16vy0dJHmwQj0,7017
56
- loaders/muse_quota_probe.py,sha256=0qCZYMhBDdljjgbbWb3cDEeKeG2q9L-x6f0pH5L_jUk,9226
56
+ loaders/muse_quota_probe.py,sha256=4q6TzlaV6HLqhDrvLYXBg9mav_9zV__WTLyOiXQsWi4,9304
57
57
  menubar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
58
  menubar/actions.py,sha256=F_4CPe_5r0MgHi0LR84VTo8GKOa42hTCYPKRqXTOJ0o,11369
59
59
  menubar/agy.py,sha256=oUZAOHsD4oqeVHUoKAYQIZGbvro3vyp_7E5f-MDiH5U,12374
@@ -86,13 +86,13 @@ panels/window_drag.py,sha256=a86iAOMEDK6AIuJr1TeGQDjP7MsN3Y0Qd5A6RKI6I5A,1776
86
86
  quota/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
87
  quota/agy_window_keeper.py,sha256=5FXQ_Ak95XMcKNN_cDLqbAgu6naQ9neeNMX5HY3VgZo,4919
88
88
  quota/burn_rate.py,sha256=E7ivzuylivRfdP1tlzP2vlZp9nDB0ynPGgmvKKMY-cs,3909
89
- quota/codex_window_keeper.py,sha256=gWTX8kZ0bnb9NeDPTPtecOP5HgeIg8a_RvarPwimcg8,9531
89
+ quota/codex_window_keeper.py,sha256=hF6AR0Z1kJ1W8IMPXC8WzAR-z3fg0mTxLCKUqlICBoo,9625
90
90
  quota/keeper_outcome.py,sha256=XesRXAe3xiYN1jHzVBa5WN9PjnamzEFbZVz3L3nOLU4,6641
91
91
  quota/notifications.py,sha256=fwUG5yQOpDptNMlJ4KBIkfpqf9wYeFbEOfrlHC_Y2qU,3632
92
92
  quota/quota_snapshot.py,sha256=pgK2usEEF0-LamtiYC-JLEw7IE4oh74Y3R64j5ZZkS8,989
93
93
  quota/status_payload.py,sha256=C3w8PXth0CpcSFAjZUGoePTdqSSkt3CpH9wACA1rY5c,7318
94
94
  quota/usage_rate.py,sha256=bVRuRm6NSVfHKtJMHZepZYeKfVutFXGXpV_W733nDtM,4518
95
- quota/window_keeper.py,sha256=LV5LvZw8NljVyvLdu4kvOmXKE41McEgPu5AnYocChvk,9292
95
+ quota/window_keeper.py,sha256=83g5beJgyPl1YuyUS3xnZNKPMvttegk1mhpvfTHuI4Q,9536
96
96
  tui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
97
  tui/app.py,sha256=UptQ1SgP5ZhtvqZK4n__y4Hd9JGcul05xdVjctd0tM0,8682
98
98
  tui/sprite.py,sha256=jxnUxCZAjvxN3NK28micu46fqRnp09YP99zAlraV5Nc,5714
@@ -113,9 +113,9 @@ updates/whats_new.py,sha256=FVYkydixTh3uXq65_LqbZVv-nnIrkURrOsGlDTLQ4tc,4277
113
113
  usage_app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
114
114
  usage_app/cli.py,sha256=pQN5hOFtN9horGzL0N-LK4ahzAXUqiRI5C7lRbOnFd4,26929
115
115
  usage_app/doctor.py,sha256=En9UNOpTtwWeYgKAhUY_QT_hthQ6YqVbV2_WXHQfdBQ,17121
116
- usage_cli-0.34.2.data/data/share/usage/i18n.json,sha256=nGo9Fw9hYvMm6rqAOR-hUmtue9BsjeGLpHlusuTJS1w,217463
117
- usage_cli-0.34.2.dist-info/licenses/LICENSE,sha256=GbPiL4Am4oP7rnqY-G84xpiYOGUGqVZiqnKFBDDWRFo,34586
118
- usage_cli-0.34.2.dist-info/licenses/THIRD_PARTY_NOTICES.md,sha256=zX2bDKg55DK-e1Q_lJynWotpjynY2wSUhgRMbjTeNps,4565
116
+ usage_cli-0.34.4.data/data/share/usage/i18n.json,sha256=nGo9Fw9hYvMm6rqAOR-hUmtue9BsjeGLpHlusuTJS1w,217463
117
+ usage_cli-0.34.4.dist-info/licenses/LICENSE,sha256=GbPiL4Am4oP7rnqY-G84xpiYOGUGqVZiqnKFBDDWRFo,34586
118
+ usage_cli-0.34.4.dist-info/licenses/THIRD_PARTY_NOTICES.md,sha256=zX2bDKg55DK-e1Q_lJynWotpjynY2wSUhgRMbjTeNps,4565
119
119
  usage_common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
120
120
  usage_common/app_version.py,sha256=ONJp-U7NskrFIeX2MOH4PyqaxRw9oMT9sztrg0oLwuk,816
121
121
  usage_common/i18n.py,sha256=bIT-_FRBxIyIPUAJD7fna6UkZ2kmNLyVepdPoIBrzgE,3453
@@ -144,12 +144,12 @@ wintray/menu.py,sha256=AJra7amA9Yf1mrq-EFEwnPTqzl9cLWj445-sURsDmyc,6190
144
144
  wintray/prefs.py,sha256=Y2KwAkxSV20230E6ZHtJmC3QaeH_FeaOVf7LD277kVw,750
145
145
  wintray/quota_label.py,sha256=FnA-zkn7eU2nZmJZhqSBzx4OlyPpDa0tbdwGYD1t9qA,7379
146
146
  wintray/taskbar_menu.py,sha256=jrc7O2Mdd9-_OjSfIGTt--Ljzy45W2dNJ_l75951AyM,3920
147
- wintray/taskbar_obstacles.py,sha256=XLJsfqLtkzvgihuTYf8Rw0djSGpRI1Z1lfa0-sSgk9c,2991
147
+ wintray/taskbar_obstacles.py,sha256=fPiyRKlnFwP8r7wt-2EFmhypKOQUUa0eOW7SIsYon4U,5311
148
148
  wintray/taskbar_overlay.py,sha256=QlVLm_Vtqa6ZzyNkXZT9tpq33qjJhQmVeftkYMB3M7g,11613
149
149
  wintray/watch.py,sha256=ZceTW4xHqzAhdDJ1wi4ZBjZeIphS4dHtJ4NM4boMSuE,16376
150
150
  wintray/whats_new.py,sha256=_ZqE3VCVPYtnmFZv908a8Rprtyu9f17_xvhef4iUmNA,1761
151
- usage_cli-0.34.2.dist-info/METADATA,sha256=gqocONSe64roQ6m6fc4cK1SDuQM-V8f6GILIvgODT7k,24659
152
- usage_cli-0.34.2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
153
- usage_cli-0.34.2.dist-info/entry_points.txt,sha256=yNBXStIb4K0RBpCfJxJB8RXqAkDeCfG2JnQngyINfGg,76
154
- usage_cli-0.34.2.dist-info/top_level.txt,sha256=Cy9791hXLQi6zYem_X__ZV5Mi4uaSJzKRs7SI9Y9ais,130
155
- usage_cli-0.34.2.dist-info/RECORD,,
151
+ usage_cli-0.34.4.dist-info/METADATA,sha256=KoIz6ZW5j1OAim1I_R70hNpmbJ5LNLUMINpx0xms7o4,24659
152
+ usage_cli-0.34.4.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
153
+ usage_cli-0.34.4.dist-info/entry_points.txt,sha256=yNBXStIb4K0RBpCfJxJB8RXqAkDeCfG2JnQngyINfGg,76
154
+ usage_cli-0.34.4.dist-info/top_level.txt,sha256=Cy9791hXLQi6zYem_X__ZV5Mi4uaSJzKRs7SI9Y9ais,130
155
+ usage_cli-0.34.4.dist-info/RECORD,,
@@ -5,9 +5,11 @@
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ import ctypes as C
8
9
  import importlib
9
10
  import logging
10
11
  import threading
12
+ from functools import cache
11
13
  from pathlib import Path
12
14
 
13
15
  from wintray.taskbar_overlay import Rect
@@ -15,6 +17,76 @@ from wintray.taskbar_overlay import Rect
15
17
  logger = logging.getLogger(__name__)
16
18
 
17
19
 
20
+ class _WTSINFOEX_LEVEL1_W(C.Structure):
21
+ # Windows WCHAR and LONG stay 16/32 bits even on hosts with wider C types.
22
+ _fields_ = [
23
+ ("SessionId", C.c_uint32),
24
+ ("SessionState", C.c_int32),
25
+ ("SessionFlags", C.c_int32),
26
+ ("WinStationName", C.c_uint16 * 33),
27
+ ("UserName", C.c_uint16 * 21),
28
+ ("DomainName", C.c_uint16 * 18),
29
+ ("LogonTime", C.c_int64),
30
+ ("ConnectTime", C.c_int64),
31
+ ("DisconnectTime", C.c_int64),
32
+ ("LastInputTime", C.c_int64),
33
+ ("CurrentTime", C.c_int64),
34
+ ("IncomingBytes", C.c_uint32),
35
+ ("OutgoingBytes", C.c_uint32),
36
+ ("IncomingFrames", C.c_uint32),
37
+ ("OutgoingFrames", C.c_uint32),
38
+ ("IncomingCompressedBytes", C.c_uint32),
39
+ ("OutgoingCompressedBytes", C.c_uint32),
40
+ ]
41
+
42
+
43
+ class _WTSINFOEX_LEVEL_W(C.Union):
44
+ _fields_ = [("WTSInfoExLevel1", _WTSINFOEX_LEVEL1_W)]
45
+
46
+
47
+ class _WTSINFOEXW(C.Structure):
48
+ _fields_ = [("Level", C.c_uint32), ("Data", _WTSINFOEX_LEVEL_W)]
49
+
50
+
51
+ @cache
52
+ def _wts_api() -> C.CDLL:
53
+ # WinDLL is absent from ctypes and its stubs on non-Windows hosts.
54
+ loader = getattr(C, "WinDLL") # noqa: B009
55
+ wts: C.CDLL = loader("wtsapi32", use_last_error=True)
56
+ wts.WTSQuerySessionInformationW.argtypes = [
57
+ C.c_void_p,
58
+ C.c_uint32,
59
+ C.c_int,
60
+ C.POINTER(C.c_void_p),
61
+ C.POINTER(C.c_uint32),
62
+ ]
63
+ wts.WTSQuerySessionInformationW.restype = C.c_int
64
+ wts.WTSFreeMemory.argtypes = [C.c_void_p]
65
+ wts.WTSFreeMemory.restype = None
66
+ return wts
67
+
68
+
69
+ def _session_locked() -> bool:
70
+ try:
71
+ wts = _wts_api()
72
+ buffer = C.c_void_p()
73
+ size = C.c_uint32()
74
+ try:
75
+ if not wts.WTSQuerySessionInformationW(
76
+ None, 0xFFFFFFFF, 25, C.byref(buffer), C.byref(size)
77
+ ):
78
+ return False
79
+ if not buffer or size.value < C.sizeof(_WTSINFOEXW):
80
+ return False
81
+ info = C.cast(buffer, C.POINTER(_WTSINFOEXW)).contents
82
+ return bool(info.Level == 1 and info.Data.WTSInfoExLevel1.SessionFlags == 0)
83
+ finally:
84
+ if buffer:
85
+ wts.WTSFreeMemory(buffer)
86
+ except (OSError, AttributeError):
87
+ return False
88
+
89
+
18
90
  class TaskbarObstacles:
19
91
  def __init__(self) -> None:
20
92
  self.hwnd = 0
@@ -42,7 +114,7 @@ class TaskbarObstacles:
42
114
  )
43
115
  while not self._stop.is_set():
44
116
  hwnd = self.hwnd
45
- if hwnd:
117
+ if hwnd and not _session_locked():
46
118
  try:
47
119
  root = element.FromHandle(system.IntPtr(hwnd))
48
120
  buttons = root.FindAll(automation.TreeScope.Descendants, condition)