lux-hammer 0.3.28 → 0.3.29
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.
- checksums.yaml +4 -4
- data/.version +1 -1
- data/recipes/lib/llm/usage.rb +52 -9
- data/recipes/llm.rb +6 -3
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6dab0deb8eba80ec1aac72b16620370af2d80d2528641e9117be71df84e8d416
|
|
4
|
+
data.tar.gz: 6460bbda170ba18cedf234846e4898f1c6efc48732c81ed122fc6eaa11d237b8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c24c74422db1ac9a72b5a03ae4f7d4e56e486c7622be40f5e136c16b9a15b2a04feeb7d2f61cdfdaaeafbc5c3913e71c2f8faf567cac8ea236eaeeadcb7fcb4f
|
|
7
|
+
data.tar.gz: 823288d67fd112a118c09b3b1dd3599a3b080c5c74fd6e5a35492be182a72fa3fbe82359498e1facb0396b61773b1a51e61dba786284d2bde19426a8e3eddc81
|
data/.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.3.
|
|
1
|
+
0.3.29
|
data/recipes/lib/llm/usage.rb
CHANGED
|
@@ -64,7 +64,7 @@ module LlmUsage
|
|
|
64
64
|
# and caching would freeze the staleness note. Only the API call caches.
|
|
65
65
|
data, note = fetch_claude_usage(period: label, cache: cache, now: now)
|
|
66
66
|
notes << note unless note.nil? || note.empty?
|
|
67
|
-
rows.concat(parse_claude(data, now: now)) if data
|
|
67
|
+
rows.concat(parse_claude(data, now: now, period: label)) if data
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
if wanted.include?(:codex)
|
|
@@ -294,7 +294,7 @@ module LlmUsage
|
|
|
294
294
|
"#{mins}m"
|
|
295
295
|
end
|
|
296
296
|
|
|
297
|
-
def parse_claude(data, now: Time.now)
|
|
297
|
+
def parse_claude(data, now: Time.now, period: nil)
|
|
298
298
|
return [] unless data.is_a?(Hash)
|
|
299
299
|
|
|
300
300
|
five = data['five_hour']
|
|
@@ -329,13 +329,13 @@ module LlmUsage
|
|
|
329
329
|
month_reset: '-'
|
|
330
330
|
)
|
|
331
331
|
end
|
|
332
|
-
return rows
|
|
332
|
+
return rows + claude_scoped_rows(data['limits'], rows, now: now, period: period)
|
|
333
333
|
end
|
|
334
334
|
|
|
335
335
|
week = data['seven_day']
|
|
336
336
|
return [] unless five.is_a?(Hash) || week.is_a?(Hash)
|
|
337
337
|
|
|
338
|
-
[
|
|
338
|
+
rows = [
|
|
339
339
|
UsageRow.new(
|
|
340
340
|
name: 'Claude',
|
|
341
341
|
session_pct: format_pct(session_pct),
|
|
@@ -346,6 +346,36 @@ module LlmUsage
|
|
|
346
346
|
month_reset: month[:reset]
|
|
347
347
|
)
|
|
348
348
|
]
|
|
349
|
+
rows + claude_scoped_rows(data['limits'], rows, now: now, period: period)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Per-model weekly buckets live in `limits` as kind weekly_scoped - Fable has
|
|
353
|
+
# one, and seven_day_opus/_sonnet read null on the accounts that do. Named off
|
|
354
|
+
# the API's own display_name, so a model added later needs nothing here; a
|
|
355
|
+
# scope already carrying its own seven_day_* row is skipped rather than
|
|
356
|
+
# doubled. A weekly bucket has nothing to say in the month view, where these
|
|
357
|
+
# would be a row of dashes.
|
|
358
|
+
def claude_scoped_rows(limits, rows, now: Time.now, period: nil)
|
|
359
|
+
return [] if period_label(period) == 'month'
|
|
360
|
+
|
|
361
|
+
taken = rows.map(&:name)
|
|
362
|
+
|
|
363
|
+
Array(limits).filter_map do |limit|
|
|
364
|
+
next unless limit.is_a?(Hash) && limit['kind'] == 'weekly_scoped'
|
|
365
|
+
|
|
366
|
+
name = limit.dig('scope', 'model', 'display_name')
|
|
367
|
+
next if name.nil? || name.empty? || taken.include?(name)
|
|
368
|
+
|
|
369
|
+
UsageRow.new(
|
|
370
|
+
name: name,
|
|
371
|
+
session_pct: '-',
|
|
372
|
+
session_reset: '-',
|
|
373
|
+
week_pct: format_pct(limit['percent']),
|
|
374
|
+
week_reset: format_reset_short(limit['resets_at'], now: now),
|
|
375
|
+
month_pct: '-',
|
|
376
|
+
month_reset: '-'
|
|
377
|
+
)
|
|
378
|
+
end
|
|
349
379
|
end
|
|
350
380
|
|
|
351
381
|
def parse_codex(data, now: Time.now)
|
|
@@ -403,7 +433,7 @@ module LlmUsage
|
|
|
403
433
|
return fetch_cached(:claude, cache) { fetch_claude_oauth_usage } if period == 'month'
|
|
404
434
|
|
|
405
435
|
data, note = fetch_claude_snapshot(now: now, path: path)
|
|
406
|
-
return [data, note] if data
|
|
436
|
+
return [merge_scoped_limits(data, cache: cache), note] if data
|
|
407
437
|
|
|
408
438
|
api_data, api_note = fetch_cached(:claude, cache) { fetch_claude_oauth_usage }
|
|
409
439
|
return [api_data, api_note] if api_data
|
|
@@ -411,6 +441,18 @@ module LlmUsage
|
|
|
411
441
|
[nil, [note, api_note].compact.join('; ')]
|
|
412
442
|
end
|
|
413
443
|
|
|
444
|
+
# The statusline snapshot carries no per-model windows - Claude Code's
|
|
445
|
+
# `rate_limits` input is five_hour/seven_day and nothing else - so borrow
|
|
446
|
+
# `limits` from the API payload. Cached, so this is one call per CACHE_TTL,
|
|
447
|
+
# and silent when it fails: the snapshot's own numbers are still good.
|
|
448
|
+
def merge_scoped_limits(data, cache: true)
|
|
449
|
+
api, = fetch_cached(:claude, cache) { fetch_claude_oauth_usage }
|
|
450
|
+
limits = api['limits'] if api.is_a?(Hash)
|
|
451
|
+
return data unless limits
|
|
452
|
+
|
|
453
|
+
data.merge('limits' => limits)
|
|
454
|
+
end
|
|
455
|
+
|
|
414
456
|
def fetch_claude_snapshot(now: Time.now, path: CLAUDE_LIMITS_PATH)
|
|
415
457
|
snapshot = read_json(path)
|
|
416
458
|
return [nil, 'claude: no local snapshot (statusline writer not installed)'] unless snapshot.is_a?(Hash)
|
|
@@ -685,11 +727,12 @@ module LlmUsage
|
|
|
685
727
|
end
|
|
686
728
|
end
|
|
687
729
|
|
|
730
|
+
# A failure is cached too, or an unreachable endpoint costs its timeout on
|
|
731
|
+
# every run - and the snapshot path that now consults the API is the one
|
|
732
|
+
# that used to be instant and offline.
|
|
688
733
|
data, note = yield
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
File.write(path, JSON.generate('fetched_at' => Time.now.iso8601, 'data' => data, 'note' => note))
|
|
692
|
-
end
|
|
734
|
+
FileUtils.mkdir_p(CACHE_DIR)
|
|
735
|
+
File.write(path, JSON.generate('fetched_at' => Time.now.iso8601, 'data' => data, 'note' => note))
|
|
693
736
|
[data, note]
|
|
694
737
|
end
|
|
695
738
|
|
data/recipes/llm.rb
CHANGED
|
@@ -45,8 +45,10 @@ task :usage do
|
|
|
45
45
|
`~/.grok/logs/unified.jsonl` for the period end; tokens still sum
|
|
46
46
|
inference_done events in that log. Claude 5h/week windows come from the
|
|
47
47
|
statusline snapshot at `~/.cache/llm/claude-limits.json`, falling back to
|
|
48
|
-
the OAuth usage API.
|
|
49
|
-
|
|
48
|
+
the OAuth usage API. Per-model weekly windows (Fable) get their own row and
|
|
49
|
+
come from that API either way - the snapshot doesn't carry them. `month`
|
|
50
|
+
shows Claude extra-usage credits (API only, and only when you've enabled
|
|
51
|
+
them).
|
|
50
52
|
D
|
|
51
53
|
example 'usage'
|
|
52
54
|
example 'usage month'
|
|
@@ -740,9 +742,10 @@ namespace :prompt do
|
|
|
740
742
|
D
|
|
741
743
|
opt :claude, type: :boolean, default: false, desc: 'Claude Code hook mode'
|
|
742
744
|
opt :codex, type: :boolean, default: false, desc: 'Codex hook mode'
|
|
745
|
+
opt :grok, type: :boolean, default: false, desc: 'Grok CLI hook mode'
|
|
743
746
|
|
|
744
747
|
proc do |opts|
|
|
745
|
-
error '--claude or --
|
|
748
|
+
error '--claude, --codex or --grok required' unless opts[:claude] || opts[:codex] || opts[:grok]
|
|
746
749
|
|
|
747
750
|
raw = opts[:stdin].to_s
|
|
748
751
|
prompt = begin
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lux-hammer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.29
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dino Reic
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|