token-tracker 0.2.3__tar.gz → 0.2.4__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.
- {token_tracker-0.2.3 → token_tracker-0.2.4}/PKG-INFO +1 -1
- {token_tracker-0.2.3 → token_tracker-0.2.4}/pyproject.toml +1 -1
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/cli.py +78 -13
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/ui/tables.py +27 -8
- {token_tracker-0.2.3 → token_tracker-0.2.4}/token_tracker.egg-info/PKG-INFO +1 -1
- {token_tracker-0.2.3 → token_tracker-0.2.4}/README.md +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/setup.cfg +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/__init__.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/adapters/__init__.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/adapters/claude.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/adapters/codex.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/adapters/rate_limits.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/adapters/registry.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/adapters/types.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/analyzer/__init__.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/analyzer/aggregator.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/analyzer/blocks.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/analyzer/cost.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/hooks.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/src/ui/__init__.py +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/token_tracker.egg-info/SOURCES.txt +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/token_tracker.egg-info/dependency_links.txt +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/token_tracker.egg-info/entry_points.txt +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/token_tracker.egg-info/requires.txt +0 -0
- {token_tracker-0.2.3 → token_tracker-0.2.4}/token_tracker.egg-info/top_level.txt +0 -0
|
@@ -64,48 +64,96 @@ def _build_agent_data(agent_id: str, agent_name: str) -> dict | None:
|
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
+
def _initial_agent_index(agents) -> int:
|
|
68
|
+
import os
|
|
69
|
+
|
|
70
|
+
preferred = None
|
|
71
|
+
if os.environ.get("CODEX_THREAD_ID") or os.environ.get("CODEX_SANDBOX"):
|
|
72
|
+
preferred = "codex"
|
|
73
|
+
elif os.environ.get("CLAUDE_CONFIG_DIR") or os.environ.get("CLAUDECODE"):
|
|
74
|
+
preferred = "claude-code"
|
|
75
|
+
|
|
76
|
+
if preferred:
|
|
77
|
+
for i, agent in enumerate(agents):
|
|
78
|
+
if agent.id == preferred:
|
|
79
|
+
return i
|
|
80
|
+
return 0
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _fit_screen(text: str, height: int, scroll_offset: int) -> tuple[str, int]:
|
|
84
|
+
lines = text.splitlines()
|
|
85
|
+
if not lines:
|
|
86
|
+
return "", 0
|
|
87
|
+
max_body = max(1, height - 1)
|
|
88
|
+
max_scroll = max(0, len(lines) - max_body)
|
|
89
|
+
scroll_offset = max(0, min(scroll_offset, max_scroll))
|
|
90
|
+
visible = lines[:1] + lines[1 + scroll_offset:1 + scroll_offset + max_body - 1]
|
|
91
|
+
return "\n".join(visible), max_scroll
|
|
92
|
+
|
|
93
|
+
|
|
67
94
|
def _show_interactive_dashboard(agents):
|
|
68
95
|
import tty
|
|
69
96
|
import termios
|
|
97
|
+
import shutil
|
|
70
98
|
from io import StringIO
|
|
71
99
|
from rich.console import Console as RichConsole
|
|
72
100
|
import src.ui.tables as _tables
|
|
73
101
|
|
|
74
102
|
agent_names = [a.name for a in agents]
|
|
75
|
-
current =
|
|
103
|
+
current = _initial_agent_index(agents)
|
|
104
|
+
scroll_offset = 0
|
|
76
105
|
orig = _tables.console
|
|
77
106
|
|
|
78
|
-
sys.stdout.write("\033[?1049h\033[?25l")
|
|
79
|
-
|
|
80
|
-
sys.stdout.flush()
|
|
81
|
-
cache = {a.id: _build_agent_data(a.id, a.name) for a in agents}
|
|
107
|
+
sys.stdout.write("\033[?1049h\033[?7l\033[2J\033[3J\033[H\033[?25l")
|
|
108
|
+
cache = {}
|
|
82
109
|
|
|
83
110
|
try:
|
|
84
111
|
while True:
|
|
112
|
+
agent = agents[current]
|
|
113
|
+
if agent.id not in cache:
|
|
114
|
+
sys.stdout.write("\033[2J\033[3J\033[H\033[2m加载数据...\033[0m")
|
|
115
|
+
sys.stdout.flush()
|
|
116
|
+
cache[agent.id] = _build_agent_data(agent.id, agent.name)
|
|
117
|
+
|
|
118
|
+
size = shutil.get_terminal_size((80, 24))
|
|
119
|
+
width = size.columns
|
|
120
|
+
height = size.lines
|
|
121
|
+
|
|
85
122
|
buf = StringIO()
|
|
86
123
|
_tables.console = RichConsole(
|
|
87
|
-
file=buf, width=
|
|
124
|
+
file=buf, width=width, force_terminal=True,
|
|
88
125
|
)
|
|
89
126
|
render_tab_bar(agent_names, current)
|
|
90
|
-
data = cache[
|
|
127
|
+
data = cache[agent.id]
|
|
91
128
|
if data:
|
|
92
|
-
render_dashboard(**data)
|
|
129
|
+
render_dashboard(**data, session_limit=10, top_margin=False)
|
|
93
130
|
else:
|
|
94
131
|
_tables.console.print(f"[yellow]暂无数据[/yellow]")
|
|
95
132
|
_tables.console = orig
|
|
96
133
|
|
|
97
|
-
|
|
134
|
+
screen, max_scroll = _fit_screen(buf.getvalue(), height, scroll_offset)
|
|
135
|
+
sys.stdout.write("\033[2J\033[3J\033[H" + screen)
|
|
98
136
|
sys.stdout.flush()
|
|
99
137
|
|
|
100
138
|
key = _read_key(tty, termios)
|
|
101
139
|
if key == "left":
|
|
102
140
|
current = (current - 1) % len(agents)
|
|
141
|
+
scroll_offset = 0
|
|
103
142
|
elif key == "right":
|
|
104
143
|
current = (current + 1) % len(agents)
|
|
144
|
+
scroll_offset = 0
|
|
145
|
+
elif key == "up":
|
|
146
|
+
scroll_offset = max(0, scroll_offset - 1)
|
|
147
|
+
elif key == "down":
|
|
148
|
+
scroll_offset = min(max_scroll, scroll_offset + 1)
|
|
149
|
+
elif key == "page_up":
|
|
150
|
+
scroll_offset = max(0, scroll_offset - max(1, height - 3))
|
|
151
|
+
elif key == "page_down":
|
|
152
|
+
scroll_offset = min(max_scroll, scroll_offset + max(1, height - 3))
|
|
105
153
|
elif key == "quit":
|
|
106
154
|
break
|
|
107
155
|
finally:
|
|
108
|
-
sys.stdout.write("\033[?25h\033[?1049l")
|
|
156
|
+
sys.stdout.write("\033[?7h\033[?25h\033[?1049l")
|
|
109
157
|
sys.stdout.flush()
|
|
110
158
|
_tables.console = orig
|
|
111
159
|
|
|
@@ -128,11 +176,27 @@ def _read_key(tty, termios):
|
|
|
128
176
|
return "left"
|
|
129
177
|
if ch3 == b"C":
|
|
130
178
|
return "right"
|
|
179
|
+
if ch3 == b"A":
|
|
180
|
+
return "up"
|
|
181
|
+
if ch3 == b"B":
|
|
182
|
+
return "down"
|
|
183
|
+
if ch3 in (b"5", b"6"):
|
|
184
|
+
if select.select([fd], [], [], 0.05)[0]:
|
|
185
|
+
_os.read(fd, 1)
|
|
186
|
+
return "page_up" if ch3 == b"5" else "page_down"
|
|
131
187
|
return "other"
|
|
132
|
-
if ch
|
|
188
|
+
if ch == b"h":
|
|
133
189
|
return "left"
|
|
134
|
-
if ch
|
|
190
|
+
if ch == b"l":
|
|
135
191
|
return "right"
|
|
192
|
+
if ch == b"k":
|
|
193
|
+
return "up"
|
|
194
|
+
if ch == b"j":
|
|
195
|
+
return "down"
|
|
196
|
+
if ch == b"b":
|
|
197
|
+
return "page_up"
|
|
198
|
+
if ch == b"f":
|
|
199
|
+
return "page_down"
|
|
136
200
|
if ch in (b"q", b"Q", b"\x03"):
|
|
137
201
|
return "quit"
|
|
138
202
|
return "other"
|
|
@@ -166,7 +230,8 @@ def main():
|
|
|
166
230
|
|
|
167
231
|
agent_ids = {a.id for a in agents}
|
|
168
232
|
|
|
169
|
-
|
|
233
|
+
if command != "dashboard":
|
|
234
|
+
console.print(f"[dim]检测到: {', '.join(a.name + ' ✓' for a in agents)}[/dim]")
|
|
170
235
|
|
|
171
236
|
if not is_setup():
|
|
172
237
|
setup(auto=True)
|
|
@@ -184,14 +184,21 @@ def _render_week_section(lines: Text, week: WeeklyStats,
|
|
|
184
184
|
def render_tab_bar(agent_names: list[str], current: int) -> None:
|
|
185
185
|
line = Text()
|
|
186
186
|
line.append(" ")
|
|
187
|
+
compact = console.width < 72
|
|
187
188
|
for i, name in enumerate(agent_names):
|
|
188
189
|
if i > 0:
|
|
189
190
|
line.append(" │ ", style=_S.dim)
|
|
191
|
+
label = AGENT_SHORT.get("claude-code" if name == "Claude Code" else name.lower(), name)
|
|
192
|
+
if compact and name == "Claude Code":
|
|
193
|
+
label = "CC"
|
|
194
|
+
elif compact:
|
|
195
|
+
label = name[:8]
|
|
190
196
|
if i == current:
|
|
191
|
-
line.append(f" {
|
|
197
|
+
line.append(f" {label} ", style="bold reverse")
|
|
192
198
|
else:
|
|
193
|
-
line.append(f" {
|
|
194
|
-
|
|
199
|
+
line.append(f" {label} ", style=_S.dim)
|
|
200
|
+
help_text = " ←→ jk q/ESC退出" if compact else " ← → 切换 ↑ ↓ 滚动 q / ESC 退出"
|
|
201
|
+
line.append(help_text, style=_S.dim)
|
|
195
202
|
console.print(line)
|
|
196
203
|
|
|
197
204
|
|
|
@@ -200,9 +207,11 @@ def _project_short(project: str) -> str:
|
|
|
200
207
|
|
|
201
208
|
|
|
202
209
|
def _render_header(agents: list[str], total_tokens: int, total_cost: float,
|
|
203
|
-
total_sessions: int, total_messages: int, days: int
|
|
210
|
+
total_sessions: int, total_messages: int, days: int,
|
|
211
|
+
top_margin: bool = True) -> None:
|
|
204
212
|
agent_text = " ".join(f"[{_S.good}]●[/{_S.good}] {a}" for a in agents)
|
|
205
|
-
|
|
213
|
+
if top_margin:
|
|
214
|
+
console.print()
|
|
206
215
|
console.print(Panel(
|
|
207
216
|
f"[bold]Token Tracker[/bold] {agent_text}",
|
|
208
217
|
border_style="blue",
|
|
@@ -262,6 +271,8 @@ def render_dashboard(
|
|
|
262
271
|
rate_limits: RateLimits | None = None,
|
|
263
272
|
p90: P90Limits | None = None,
|
|
264
273
|
agents: list[str] | None = None,
|
|
274
|
+
session_limit: int = 10,
|
|
275
|
+
top_margin: bool = True,
|
|
265
276
|
) -> None:
|
|
266
277
|
if not daily_stats:
|
|
267
278
|
console.print(f"[{_S.warn}]暂无数据[/{_S.warn}]")
|
|
@@ -272,7 +283,15 @@ def render_dashboard(
|
|
|
272
283
|
total_msgs = sum(s.message_count for s in daily_stats)
|
|
273
284
|
total_sessions = sum(s.session_count for s in daily_stats)
|
|
274
285
|
|
|
275
|
-
_render_header(
|
|
286
|
+
_render_header(
|
|
287
|
+
agents or ["Claude Code"],
|
|
288
|
+
total_tokens,
|
|
289
|
+
total_cost,
|
|
290
|
+
total_sessions,
|
|
291
|
+
total_msgs,
|
|
292
|
+
len(daily_stats),
|
|
293
|
+
top_margin=top_margin,
|
|
294
|
+
)
|
|
276
295
|
|
|
277
296
|
# --- 本月概览 ---
|
|
278
297
|
if monthly_stats:
|
|
@@ -300,8 +319,8 @@ def render_dashboard(
|
|
|
300
319
|
_render_idle_panel(rate_limits, cur_week, last_week)
|
|
301
320
|
|
|
302
321
|
# --- 最近十条会话 ---
|
|
303
|
-
if sessions:
|
|
304
|
-
_render_recent_sessions(sessions[:
|
|
322
|
+
if sessions and session_limit > 0:
|
|
323
|
+
_render_recent_sessions(sessions[:session_limit])
|
|
305
324
|
|
|
306
325
|
console.print()
|
|
307
326
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|