klaude-code 2.7.0__py3-none-any.whl → 2.8.1__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.
- klaude_code/auth/AGENTS.md +325 -0
- klaude_code/auth/__init__.py +17 -1
- klaude_code/auth/antigravity/__init__.py +20 -0
- klaude_code/auth/antigravity/exceptions.py +17 -0
- klaude_code/auth/antigravity/oauth.py +320 -0
- klaude_code/auth/antigravity/pkce.py +25 -0
- klaude_code/auth/antigravity/token_manager.py +45 -0
- klaude_code/auth/base.py +4 -0
- klaude_code/auth/claude/oauth.py +29 -9
- klaude_code/auth/codex/exceptions.py +4 -0
- klaude_code/cli/auth_cmd.py +53 -3
- klaude_code/cli/cost_cmd.py +83 -160
- klaude_code/cli/list_model.py +50 -0
- klaude_code/cli/main.py +2 -2
- klaude_code/config/assets/builtin_config.yaml +108 -0
- klaude_code/config/builtin_config.py +5 -11
- klaude_code/config/config.py +24 -10
- klaude_code/const.py +2 -1
- klaude_code/core/agent.py +5 -1
- klaude_code/core/agent_profile.py +29 -33
- klaude_code/core/compaction/AGENTS.md +112 -0
- klaude_code/core/compaction/__init__.py +11 -0
- klaude_code/core/compaction/compaction.py +705 -0
- klaude_code/core/compaction/overflow.py +30 -0
- klaude_code/core/compaction/prompts.py +97 -0
- klaude_code/core/executor.py +121 -2
- klaude_code/core/manager/llm_clients.py +5 -0
- klaude_code/core/manager/llm_clients_builder.py +14 -2
- klaude_code/core/prompts/prompt-antigravity.md +80 -0
- klaude_code/core/prompts/prompt-codex-gpt-5-2.md +335 -0
- klaude_code/core/reminders.py +7 -2
- klaude_code/core/task.py +126 -0
- klaude_code/core/tool/file/edit_tool.py +1 -2
- klaude_code/core/tool/todo/todo_write_tool.py +1 -1
- klaude_code/core/turn.py +3 -1
- klaude_code/llm/antigravity/__init__.py +3 -0
- klaude_code/llm/antigravity/client.py +558 -0
- klaude_code/llm/antigravity/input.py +261 -0
- klaude_code/llm/registry.py +1 -0
- klaude_code/protocol/commands.py +1 -0
- klaude_code/protocol/events.py +18 -0
- klaude_code/protocol/llm_param.py +1 -0
- klaude_code/protocol/message.py +23 -1
- klaude_code/protocol/op.py +29 -1
- klaude_code/protocol/op_handler.py +10 -0
- klaude_code/session/export.py +308 -299
- klaude_code/session/session.py +36 -0
- klaude_code/session/templates/export_session.html +430 -134
- klaude_code/skill/assets/create-plan/SKILL.md +6 -6
- klaude_code/tui/command/__init__.py +6 -0
- klaude_code/tui/command/compact_cmd.py +32 -0
- klaude_code/tui/command/continue_cmd.py +34 -0
- klaude_code/tui/command/fork_session_cmd.py +110 -14
- klaude_code/tui/command/model_picker.py +5 -1
- klaude_code/tui/command/thinking_cmd.py +1 -1
- klaude_code/tui/commands.py +6 -0
- klaude_code/tui/components/rich/markdown.py +119 -12
- klaude_code/tui/components/rich/theme.py +10 -2
- klaude_code/tui/components/tools.py +39 -25
- klaude_code/tui/components/user_input.py +1 -1
- klaude_code/tui/input/__init__.py +5 -2
- klaude_code/tui/input/drag_drop.py +6 -57
- klaude_code/tui/input/key_bindings.py +10 -0
- klaude_code/tui/input/prompt_toolkit.py +19 -6
- klaude_code/tui/machine.py +25 -0
- klaude_code/tui/renderer.py +68 -4
- klaude_code/tui/runner.py +18 -2
- klaude_code/tui/terminal/image.py +72 -10
- klaude_code/tui/terminal/selector.py +31 -7
- {klaude_code-2.7.0.dist-info → klaude_code-2.8.1.dist-info}/METADATA +1 -1
- {klaude_code-2.7.0.dist-info → klaude_code-2.8.1.dist-info}/RECORD +73 -56
- klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +0 -117
- {klaude_code-2.7.0.dist-info → klaude_code-2.8.1.dist-info}/WHEEL +0 -0
- {klaude_code-2.7.0.dist-info → klaude_code-2.8.1.dist-info}/entry_points.txt +0 -0
klaude_code/cli/cost_cmd.py
CHANGED
|
@@ -45,12 +45,52 @@ class ModelUsageStats:
|
|
|
45
45
|
self.cost_usd += usage.total_cost
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
ModelKey = tuple[str, str] # (model_name, provider)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def group_models_by_provider(
|
|
52
|
+
models: dict[ModelKey, ModelUsageStats],
|
|
53
|
+
) -> tuple[dict[str, list[ModelUsageStats]], dict[str, ModelUsageStats]]:
|
|
54
|
+
"""Group models by provider and compute provider totals.
|
|
55
|
+
|
|
56
|
+
Returns (models_by_provider, provider_totals) where both are sorted by cost desc.
|
|
57
|
+
"""
|
|
58
|
+
models_by_provider: dict[str, list[ModelUsageStats]] = {}
|
|
59
|
+
provider_totals: dict[str, ModelUsageStats] = {}
|
|
60
|
+
|
|
61
|
+
for stats in models.values():
|
|
62
|
+
provider_key = stats.provider or "(unknown)"
|
|
63
|
+
if provider_key not in models_by_provider:
|
|
64
|
+
models_by_provider[provider_key] = []
|
|
65
|
+
provider_totals[provider_key] = ModelUsageStats(model_name=provider_key, provider=provider_key)
|
|
66
|
+
models_by_provider[provider_key].append(stats)
|
|
67
|
+
provider_totals[provider_key].input_tokens += stats.input_tokens
|
|
68
|
+
provider_totals[provider_key].output_tokens += stats.output_tokens
|
|
69
|
+
provider_totals[provider_key].cached_tokens += stats.cached_tokens
|
|
70
|
+
provider_totals[provider_key].cost_usd += stats.cost_usd
|
|
71
|
+
provider_totals[provider_key].cost_cny += stats.cost_cny
|
|
72
|
+
|
|
73
|
+
def sort_by_cost(stats: ModelUsageStats) -> tuple[float, float]:
|
|
74
|
+
return (-stats.cost_usd, -stats.cost_cny)
|
|
75
|
+
|
|
76
|
+
# Sort providers by cost, and models within each provider
|
|
77
|
+
sorted_providers = sorted(provider_totals.keys(), key=lambda p: sort_by_cost(provider_totals[p]))
|
|
78
|
+
for provider_key in models_by_provider:
|
|
79
|
+
models_by_provider[provider_key].sort(key=sort_by_cost)
|
|
80
|
+
|
|
81
|
+
# Rebuild dicts in sorted order
|
|
82
|
+
sorted_models_by_provider = {p: models_by_provider[p] for p in sorted_providers}
|
|
83
|
+
sorted_provider_totals = {p: provider_totals[p] for p in sorted_providers}
|
|
84
|
+
|
|
85
|
+
return sorted_models_by_provider, sorted_provider_totals
|
|
86
|
+
|
|
87
|
+
|
|
48
88
|
@dataclass
|
|
49
89
|
class DailyStats:
|
|
50
90
|
"""Aggregated stats for a single day."""
|
|
51
91
|
|
|
52
92
|
date: str
|
|
53
|
-
by_model: dict[
|
|
93
|
+
by_model: dict[ModelKey, ModelUsageStats] = field(default_factory=lambda: dict[ModelKey, ModelUsageStats]())
|
|
54
94
|
|
|
55
95
|
def add_task_metadata(self, meta: model.TaskMetadata, date_str: str) -> None:
|
|
56
96
|
"""Add a TaskMetadata to this day's stats."""
|
|
@@ -58,26 +98,14 @@ class DailyStats:
|
|
|
58
98
|
if not meta.usage or not meta.model_name:
|
|
59
99
|
return
|
|
60
100
|
|
|
61
|
-
model_key = meta.model_name
|
|
62
101
|
provider = meta.provider or meta.usage.provider or ""
|
|
102
|
+
model_key: ModelKey = (meta.model_name, provider)
|
|
103
|
+
|
|
63
104
|
if model_key not in self.by_model:
|
|
64
|
-
self.by_model[model_key] = ModelUsageStats(model_name=
|
|
65
|
-
elif not self.by_model[model_key].provider and provider:
|
|
66
|
-
self.by_model[model_key].provider = provider
|
|
105
|
+
self.by_model[model_key] = ModelUsageStats(model_name=meta.model_name, provider=provider)
|
|
67
106
|
|
|
68
107
|
self.by_model[model_key].add_usage(meta.usage)
|
|
69
108
|
|
|
70
|
-
def get_subtotal(self) -> ModelUsageStats:
|
|
71
|
-
"""Get subtotal across all models for this day."""
|
|
72
|
-
subtotal = ModelUsageStats(model_name="(subtotal)")
|
|
73
|
-
for stats in self.by_model.values():
|
|
74
|
-
subtotal.input_tokens += stats.input_tokens
|
|
75
|
-
subtotal.output_tokens += stats.output_tokens
|
|
76
|
-
subtotal.cached_tokens += stats.cached_tokens
|
|
77
|
-
subtotal.cost_usd += stats.cost_usd
|
|
78
|
-
subtotal.cost_cny += stats.cost_cny
|
|
79
|
-
return subtotal
|
|
80
|
-
|
|
81
109
|
|
|
82
110
|
def iter_all_sessions() -> list[tuple[str, Path]]:
|
|
83
111
|
"""Iterate over all sessions across all projects.
|
|
@@ -201,78 +229,52 @@ def render_cost_table(daily_stats: dict[str, DailyStats]) -> Table:
|
|
|
201
229
|
table.add_column("USD", justify="right")
|
|
202
230
|
table.add_column("CNY", justify="right")
|
|
203
231
|
|
|
204
|
-
# Sort dates
|
|
205
232
|
sorted_dates = sorted(daily_stats.keys())
|
|
233
|
+
global_by_model: dict[ModelKey, ModelUsageStats] = {}
|
|
234
|
+
|
|
235
|
+
def add_stats_row(stats: ModelUsageStats, date_label: str = "", prefix: str = "", bold: bool = False) -> None:
|
|
236
|
+
"""Add a single stats row to the table."""
|
|
237
|
+
usd_str, cny_str = format_cost_dual(stats.cost_usd, stats.cost_cny)
|
|
238
|
+
if prefix:
|
|
239
|
+
model_col = f"[bright_black dim]{prefix}[/bright_black dim]{stats.model_name}"
|
|
240
|
+
elif bold:
|
|
241
|
+
model_col = f"[bold]{stats.model_name}[/bold]"
|
|
242
|
+
else:
|
|
243
|
+
model_col = stats.model_name
|
|
206
244
|
|
|
207
|
-
|
|
208
|
-
|
|
245
|
+
def fmt(val: str) -> str:
|
|
246
|
+
return f"[bold]{val}[/bold]" if bold else val
|
|
209
247
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
248
|
+
table.add_row(
|
|
249
|
+
date_label,
|
|
250
|
+
model_col,
|
|
251
|
+
fmt(format_tokens(stats.input_tokens)),
|
|
252
|
+
fmt(format_tokens(stats.output_tokens)),
|
|
253
|
+
fmt(format_tokens(stats.cached_tokens)),
|
|
254
|
+
fmt(format_tokens(stats.total_tokens)),
|
|
255
|
+
fmt(usd_str),
|
|
256
|
+
fmt(cny_str),
|
|
257
|
+
)
|
|
213
258
|
|
|
214
|
-
def
|
|
215
|
-
models: dict[
|
|
259
|
+
def render_grouped(
|
|
260
|
+
models: dict[ModelKey, ModelUsageStats],
|
|
216
261
|
date_label: str = "",
|
|
217
262
|
show_subtotal: bool = True,
|
|
218
263
|
) -> None:
|
|
219
264
|
"""Render models grouped by provider with tree structure."""
|
|
220
|
-
|
|
221
|
-
models_by_provider: dict[str, list[ModelUsageStats]] = {}
|
|
222
|
-
provider_totals: dict[str, ModelUsageStats] = {}
|
|
223
|
-
for stats in models.values():
|
|
224
|
-
provider_key = stats.provider or "(unknown)"
|
|
225
|
-
if provider_key not in models_by_provider:
|
|
226
|
-
models_by_provider[provider_key] = []
|
|
227
|
-
provider_totals[provider_key] = ModelUsageStats(model_name=provider_key, provider=provider_key)
|
|
228
|
-
models_by_provider[provider_key].append(stats)
|
|
229
|
-
provider_totals[provider_key].input_tokens += stats.input_tokens
|
|
230
|
-
provider_totals[provider_key].output_tokens += stats.output_tokens
|
|
231
|
-
provider_totals[provider_key].cached_tokens += stats.cached_tokens
|
|
232
|
-
provider_totals[provider_key].cost_usd += stats.cost_usd
|
|
233
|
-
provider_totals[provider_key].cost_cny += stats.cost_cny
|
|
234
|
-
|
|
235
|
-
# Sort providers by cost, and models within each provider by cost
|
|
236
|
-
sorted_providers = sorted(provider_totals.keys(), key=lambda p: sort_by_cost(provider_totals[p]))
|
|
237
|
-
for provider_key in models_by_provider:
|
|
238
|
-
models_by_provider[provider_key].sort(key=sort_by_cost)
|
|
265
|
+
models_by_provider, provider_totals = group_models_by_provider(models)
|
|
239
266
|
|
|
240
267
|
first_row = True
|
|
241
|
-
for provider_key in
|
|
268
|
+
for provider_key, provider_models in models_by_provider.items():
|
|
242
269
|
provider_stats = provider_totals[provider_key]
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
# Provider row (bold)
|
|
246
|
-
usd_str, cny_str = format_cost_dual(provider_stats.cost_usd, provider_stats.cost_cny)
|
|
247
|
-
table.add_row(
|
|
248
|
-
date_label if first_row else "",
|
|
249
|
-
f"[bold]{provider_key}[/bold]",
|
|
250
|
-
f"[bold]{format_tokens(provider_stats.input_tokens)}[/bold]",
|
|
251
|
-
f"[bold]{format_tokens(provider_stats.output_tokens)}[/bold]",
|
|
252
|
-
f"[bold]{format_tokens(provider_stats.cached_tokens)}[/bold]",
|
|
253
|
-
f"[bold]{format_tokens(provider_stats.total_tokens)}[/bold]",
|
|
254
|
-
f"[bold]{usd_str}[/bold]",
|
|
255
|
-
f"[bold]{cny_str}[/bold]",
|
|
256
|
-
)
|
|
270
|
+
add_stats_row(provider_stats, date_label=date_label if first_row else "", bold=True)
|
|
257
271
|
first_row = False
|
|
258
272
|
|
|
259
|
-
# Model rows with tree prefix
|
|
260
273
|
for i, stats in enumerate(provider_models):
|
|
261
274
|
is_last = i == len(provider_models) - 1
|
|
262
275
|
prefix = " └─ " if is_last else " ├─ "
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
"",
|
|
266
|
-
f"[bright_black dim]{prefix}[/bright_black dim]{stats.model_name}",
|
|
267
|
-
format_tokens(stats.input_tokens),
|
|
268
|
-
format_tokens(stats.output_tokens),
|
|
269
|
-
format_tokens(stats.cached_tokens),
|
|
270
|
-
format_tokens(stats.total_tokens),
|
|
271
|
-
usd_str,
|
|
272
|
-
cny_str,
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
# Add subtotal row
|
|
276
|
+
add_stats_row(stats, prefix=prefix)
|
|
277
|
+
|
|
276
278
|
if show_subtotal:
|
|
277
279
|
subtotal = ModelUsageStats(model_name="(subtotal)")
|
|
278
280
|
for stats in models.values():
|
|
@@ -281,43 +283,29 @@ def render_cost_table(daily_stats: dict[str, DailyStats]) -> Table:
|
|
|
281
283
|
subtotal.cached_tokens += stats.cached_tokens
|
|
282
284
|
subtotal.cost_usd += stats.cost_usd
|
|
283
285
|
subtotal.cost_cny += stats.cost_cny
|
|
284
|
-
|
|
285
|
-
table.add_row(
|
|
286
|
-
"",
|
|
287
|
-
"[bold](subtotal)[/bold]",
|
|
288
|
-
f"[bold]{format_tokens(subtotal.input_tokens)}[/bold]",
|
|
289
|
-
f"[bold]{format_tokens(subtotal.output_tokens)}[/bold]",
|
|
290
|
-
f"[bold]{format_tokens(subtotal.cached_tokens)}[/bold]",
|
|
291
|
-
f"[bold]{format_tokens(subtotal.total_tokens)}[/bold]",
|
|
292
|
-
f"[bold]{usd_str}[/bold]",
|
|
293
|
-
f"[bold]{cny_str}[/bold]",
|
|
294
|
-
)
|
|
286
|
+
add_stats_row(subtotal, bold=True)
|
|
295
287
|
|
|
296
288
|
for date_str in sorted_dates:
|
|
297
289
|
day = daily_stats[date_str]
|
|
298
290
|
|
|
299
|
-
# Accumulate to global totals
|
|
300
|
-
for
|
|
301
|
-
model_key = (model_name, stats.provider or "")
|
|
291
|
+
# Accumulate to global totals
|
|
292
|
+
for model_key, stats in day.by_model.items():
|
|
302
293
|
if model_key not in global_by_model:
|
|
303
|
-
global_by_model[model_key] = ModelUsageStats(model_name=model_name, provider=stats.provider)
|
|
294
|
+
global_by_model[model_key] = ModelUsageStats(model_name=stats.model_name, provider=stats.provider)
|
|
304
295
|
global_by_model[model_key].input_tokens += stats.input_tokens
|
|
305
296
|
global_by_model[model_key].output_tokens += stats.output_tokens
|
|
306
297
|
global_by_model[model_key].cached_tokens += stats.cached_tokens
|
|
307
298
|
global_by_model[model_key].cost_usd += stats.cost_usd
|
|
308
299
|
global_by_model[model_key].cost_cny += stats.cost_cny
|
|
309
300
|
|
|
310
|
-
|
|
311
|
-
render_by_provider(day.by_model, date_label=format_date_display(date_str))
|
|
301
|
+
render_grouped(day.by_model, date_label=format_date_display(date_str))
|
|
312
302
|
|
|
313
|
-
# Add separator between days
|
|
314
303
|
if date_str != sorted_dates[-1]:
|
|
315
304
|
table.add_section()
|
|
316
305
|
|
|
317
|
-
#
|
|
306
|
+
# Total section
|
|
318
307
|
table.add_section()
|
|
319
308
|
|
|
320
|
-
# Build date range label for Total
|
|
321
309
|
if sorted_dates:
|
|
322
310
|
first_date = format_date_display(sorted_dates[0])
|
|
323
311
|
last_date = format_date_display(sorted_dates[-1])
|
|
@@ -328,64 +316,10 @@ def render_cost_table(daily_stats: dict[str, DailyStats]) -> Table:
|
|
|
328
316
|
else:
|
|
329
317
|
total_label = "[bold]Total[/bold]"
|
|
330
318
|
|
|
331
|
-
# Group models by provider
|
|
332
|
-
models_by_provider: dict[str, list[ModelUsageStats]] = {}
|
|
333
|
-
provider_totals: dict[str, ModelUsageStats] = {}
|
|
334
|
-
for stats in global_by_model.values():
|
|
335
|
-
provider_key = stats.provider or "(unknown)"
|
|
336
|
-
if provider_key not in models_by_provider:
|
|
337
|
-
models_by_provider[provider_key] = []
|
|
338
|
-
provider_totals[provider_key] = ModelUsageStats(model_name=provider_key, provider=provider_key)
|
|
339
|
-
models_by_provider[provider_key].append(stats)
|
|
340
|
-
provider_totals[provider_key].input_tokens += stats.input_tokens
|
|
341
|
-
provider_totals[provider_key].output_tokens += stats.output_tokens
|
|
342
|
-
provider_totals[provider_key].cached_tokens += stats.cached_tokens
|
|
343
|
-
provider_totals[provider_key].cost_usd += stats.cost_usd
|
|
344
|
-
provider_totals[provider_key].cost_cny += stats.cost_cny
|
|
345
|
-
|
|
346
|
-
# Sort providers by cost, and models within each provider by cost
|
|
347
|
-
sorted_providers = sorted(provider_totals.keys(), key=lambda p: sort_by_cost(provider_totals[p]))
|
|
348
|
-
for provider_key in models_by_provider:
|
|
349
|
-
models_by_provider[provider_key].sort(key=sort_by_cost)
|
|
350
|
-
|
|
351
|
-
# Add total label row
|
|
352
319
|
table.add_row(total_label, "", "", "", "", "", "", "")
|
|
320
|
+
render_grouped(global_by_model, show_subtotal=False)
|
|
353
321
|
|
|
354
|
-
#
|
|
355
|
-
for provider_key in sorted_providers:
|
|
356
|
-
provider_stats = provider_totals[provider_key]
|
|
357
|
-
models = models_by_provider[provider_key]
|
|
358
|
-
|
|
359
|
-
# Provider row (bold)
|
|
360
|
-
usd_str, cny_str = format_cost_dual(provider_stats.cost_usd, provider_stats.cost_cny)
|
|
361
|
-
table.add_row(
|
|
362
|
-
"",
|
|
363
|
-
f"[bold]{provider_key}[/bold]",
|
|
364
|
-
f"[bold]{format_tokens(provider_stats.input_tokens)}[/bold]",
|
|
365
|
-
f"[bold]{format_tokens(provider_stats.output_tokens)}[/bold]",
|
|
366
|
-
f"[bold]{format_tokens(provider_stats.cached_tokens)}[/bold]",
|
|
367
|
-
f"[bold]{format_tokens(provider_stats.total_tokens)}[/bold]",
|
|
368
|
-
f"[bold]{usd_str}[/bold]",
|
|
369
|
-
f"[bold]{cny_str}[/bold]",
|
|
370
|
-
)
|
|
371
|
-
|
|
372
|
-
# Model rows with tree prefix
|
|
373
|
-
for i, stats in enumerate(models):
|
|
374
|
-
is_last = i == len(models) - 1
|
|
375
|
-
prefix = " └─ " if is_last else " ├─ "
|
|
376
|
-
usd_str, cny_str = format_cost_dual(stats.cost_usd, stats.cost_cny)
|
|
377
|
-
table.add_row(
|
|
378
|
-
"",
|
|
379
|
-
f"[bright_black dim]{prefix}[/bright_black dim]{stats.model_name}",
|
|
380
|
-
format_tokens(stats.input_tokens),
|
|
381
|
-
format_tokens(stats.output_tokens),
|
|
382
|
-
format_tokens(stats.cached_tokens),
|
|
383
|
-
format_tokens(stats.total_tokens),
|
|
384
|
-
usd_str,
|
|
385
|
-
cny_str,
|
|
386
|
-
)
|
|
387
|
-
|
|
388
|
-
# Add grand total row
|
|
322
|
+
# Grand total
|
|
389
323
|
grand_total = ModelUsageStats(model_name="(total)")
|
|
390
324
|
for stats in global_by_model.values():
|
|
391
325
|
grand_total.input_tokens += stats.input_tokens
|
|
@@ -393,18 +327,7 @@ def render_cost_table(daily_stats: dict[str, DailyStats]) -> Table:
|
|
|
393
327
|
grand_total.cached_tokens += stats.cached_tokens
|
|
394
328
|
grand_total.cost_usd += stats.cost_usd
|
|
395
329
|
grand_total.cost_cny += stats.cost_cny
|
|
396
|
-
|
|
397
|
-
usd_str, cny_str = format_cost_dual(grand_total.cost_usd, grand_total.cost_cny)
|
|
398
|
-
table.add_row(
|
|
399
|
-
"",
|
|
400
|
-
"[bold](total)[/bold]",
|
|
401
|
-
f"[bold]{format_tokens(grand_total.input_tokens)}[/bold]",
|
|
402
|
-
f"[bold]{format_tokens(grand_total.output_tokens)}[/bold]",
|
|
403
|
-
f"[bold]{format_tokens(grand_total.cached_tokens)}[/bold]",
|
|
404
|
-
f"[bold]{format_tokens(grand_total.total_tokens)}[/bold]",
|
|
405
|
-
f"[bold]{usd_str}[/bold]",
|
|
406
|
-
f"[bold]{cny_str}[/bold]",
|
|
407
|
-
)
|
|
330
|
+
add_stats_row(grand_total, bold=True)
|
|
408
331
|
|
|
409
332
|
return table
|
|
410
333
|
|
klaude_code/cli/list_model.py
CHANGED
|
@@ -121,6 +121,53 @@ def _get_claude_status_rows() -> list[tuple[Text, Text]]:
|
|
|
121
121
|
return rows
|
|
122
122
|
|
|
123
123
|
|
|
124
|
+
def _get_antigravity_status_rows() -> list[tuple[Text, Text]]:
|
|
125
|
+
"""Get Antigravity OAuth login status as (label, value) tuples for table display."""
|
|
126
|
+
from klaude_code.auth.antigravity.token_manager import AntigravityTokenManager
|
|
127
|
+
|
|
128
|
+
rows: list[tuple[Text, Text]] = []
|
|
129
|
+
token_manager = AntigravityTokenManager()
|
|
130
|
+
state = token_manager.get_state()
|
|
131
|
+
|
|
132
|
+
if state is None:
|
|
133
|
+
rows.append(
|
|
134
|
+
(
|
|
135
|
+
Text("Status", style=ThemeKey.CONFIG_PARAM_LABEL),
|
|
136
|
+
Text.assemble(
|
|
137
|
+
("Not logged in", ThemeKey.CONFIG_STATUS_ERROR),
|
|
138
|
+
(" (run 'klaude login antigravity' to authenticate)", "dim"),
|
|
139
|
+
),
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
elif state.is_expired():
|
|
143
|
+
rows.append(
|
|
144
|
+
(
|
|
145
|
+
Text("Status", style=ThemeKey.CONFIG_PARAM_LABEL),
|
|
146
|
+
Text.assemble(
|
|
147
|
+
("Token expired", ThemeKey.CONFIG_STATUS_ERROR),
|
|
148
|
+
(" (will refresh automatically on use; run 'klaude login antigravity' if refresh fails)", "dim"),
|
|
149
|
+
),
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
else:
|
|
153
|
+
expires_dt = datetime.datetime.fromtimestamp(state.expires_at, tz=datetime.UTC)
|
|
154
|
+
email_info = f", email: {state.email}" if state.email else ""
|
|
155
|
+
rows.append(
|
|
156
|
+
(
|
|
157
|
+
Text("Status", style=ThemeKey.CONFIG_PARAM_LABEL),
|
|
158
|
+
Text.assemble(
|
|
159
|
+
("Logged in", ThemeKey.CONFIG_STATUS_OK),
|
|
160
|
+
(
|
|
161
|
+
f" (project: {state.project_id}{email_info}, expires: {expires_dt.strftime('%Y-%m-%d %H:%M UTC')})",
|
|
162
|
+
"dim",
|
|
163
|
+
),
|
|
164
|
+
),
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
return rows
|
|
169
|
+
|
|
170
|
+
|
|
124
171
|
def mask_api_key(api_key: str | None) -> str:
|
|
125
172
|
"""Mask API key to show only first 6 and last 6 characters with *** in between"""
|
|
126
173
|
if not api_key:
|
|
@@ -234,6 +281,9 @@ def _build_provider_info_panel(provider: ProviderConfig, available: bool) -> Quo
|
|
|
234
281
|
if provider.protocol == LLMClientProtocol.CLAUDE_OAUTH:
|
|
235
282
|
for label, value in _get_claude_status_rows():
|
|
236
283
|
info_table.add_row(label, value)
|
|
284
|
+
if provider.protocol == LLMClientProtocol.ANTIGRAVITY:
|
|
285
|
+
for label, value in _get_antigravity_status_rows():
|
|
286
|
+
info_table.add_row(label, value)
|
|
237
287
|
|
|
238
288
|
return Quote(
|
|
239
289
|
Group(title, info_table),
|
klaude_code/cli/main.py
CHANGED
|
@@ -34,7 +34,7 @@ def _build_env_help() -> str:
|
|
|
34
34
|
"Tool limits (Read):",
|
|
35
35
|
" KLAUDE_READ_GLOBAL_LINE_CAP Max lines to read (default: 2000)",
|
|
36
36
|
" KLAUDE_READ_MAX_CHARS Max total chars to read (default: 50000)",
|
|
37
|
-
" KLAUDE_READ_MAX_IMAGE_BYTES Max image bytes to read (default:
|
|
37
|
+
" KLAUDE_READ_MAX_IMAGE_BYTES Max image bytes to read (default: 64MB)",
|
|
38
38
|
" KLAUDE_IMAGE_OUTPUT_MAX_BYTES Max decoded image bytes (default: 64MB)",
|
|
39
39
|
]
|
|
40
40
|
)
|
|
@@ -340,7 +340,7 @@ def main_callback(
|
|
|
340
340
|
)
|
|
341
341
|
|
|
342
342
|
if log_path:
|
|
343
|
-
log(f"Debug log: {log_path}", style="
|
|
343
|
+
log(f"Debug log: {log_path}", style="red")
|
|
344
344
|
|
|
345
345
|
asyncio.run(
|
|
346
346
|
run_interactive(
|