@vibe-cafe/vibe-usage 0.10.3 → 0.10.4

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.
package/README.md CHANGED
@@ -60,7 +60,7 @@ npx @vibe-cafe/vibe-usage status # Show config & detected tools
60
60
  | OpenClaw | `~/.openclaw/agents/`, `~/.openclaw-<profile>/agents/` (profile deployments) |
61
61
  | pi | `~/.pi/agent/sessions/` |
62
62
  | Qwen Code | `~/.qwen/tmp/` |
63
- | Kimi Code | Current `~/.kimi-code/sessions/wd_<slug>_<hash>/session_<id>/agents/<agent>/wire.jsonl` (`usage.record` deltas, including retry/compaction scope and cache creation; main/subagent wires form one session), with project names from `session_index.jsonl`; legacy `~/.kimi/sessions/` remains supported |
63
+ | Kimi Code | Current `~/.kimi-code/sessions/wd_<slug>_<hash>/session_<id>/agents/<agent>/wire.jsonl` (`usage.record` deltas, including retry/compaction scope and cache creation; main/subagent wires form one session), data root resolved via `$KIMI_CODE_HOME` like the CLI itself, with project names from `session_index.jsonl`; legacy `~/.kimi/sessions/` is parsed alongside (`kimi migrate` never carries usage over, so both stores are always merged) |
64
64
  | Amp | `~/.local/share/amp/threads/` |
65
65
  | Droid | `~/.factory/sessions/` |
66
66
  | Hermes | `~/.hermes/state.db` + `~/.hermes/profiles/<name>/state.db` (SQLite, multi-profile) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibe-cafe/vibe-usage",
3
- "version": "0.10.3",
3
+ "version": "0.10.4",
4
4
  "description": "Track your AI coding tool token usage and sync to vibecafe.ai",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -27,14 +27,25 @@ import { aggregateToBuckets, extractSessions } from './index.js';
27
27
  * with a different envelope (StatusUpdate.payload.token_usage, float-second
28
28
  * `timestamp`) and the model in ~/.kimi/config.toml. Kept for users who have
29
29
  * not migrated; see parseLegacyKimi() below.
30
+ *
31
+ * Both stores are always parsed and merged. `kimi migrate` translates legacy
32
+ * context.jsonl into the new wire format but DROPS the `_usage` records, so
33
+ * historical token usage only ever exists in the legacy store — parsing both
34
+ * cannot double-count, while skipping legacy whenever ~/.kimi-code has any
35
+ * session silently loses a migrated user's entire history.
30
36
  */
31
37
 
32
38
  // ---------------------------------------------------------------------------
33
39
  // Current format: ~/.kimi-code
34
40
  // ---------------------------------------------------------------------------
35
41
 
36
- // VIBE_USAGE_KIMI_CODE_DIR overrides the root (test hook).
37
- const KIMI_CODE_DIR = process.env.VIBE_USAGE_KIMI_CODE_DIR?.trim() || join(homedir(), '.kimi-code');
42
+ // VIBE_USAGE_KIMI_CODE_DIR overrides the root (test hook). Otherwise resolve
43
+ // the data root the same way the CLI itself does: $KIMI_CODE_HOME, then
44
+ // ~/.kimi-code. Ignoring KIMI_CODE_HOME means users with a custom home get
45
+ // zero usage parsed.
46
+ const KIMI_CODE_DIR = process.env.VIBE_USAGE_KIMI_CODE_DIR?.trim()
47
+ || process.env.KIMI_CODE_HOME?.trim()
48
+ || join(homedir(), '.kimi-code');
38
49
  const KIMI_CODE_SESSIONS_DIR = join(KIMI_CODE_DIR, 'sessions');
39
50
  const KIMI_CODE_SESSION_INDEX = join(KIMI_CODE_DIR, 'session_index.jsonl');
40
51
 
@@ -362,7 +373,8 @@ function parseLegacyKimi() {
362
373
 
363
374
  const tokenUsage = payload.token_usage;
364
375
  if (!tokenUsage) continue;
365
- if (!tokenUsage.input_other && !tokenUsage.output) continue;
376
+ if (!tokenUsage.input_other && !tokenUsage.output
377
+ && !tokenUsage.input_cache_read && !tokenUsage.input_cache_creation) continue;
366
378
 
367
379
  const messageId = payload.message_id;
368
380
  if (messageId) {
@@ -370,14 +382,21 @@ function parseLegacyKimi() {
370
382
  seenMessageIds.add(messageId);
371
383
  }
372
384
 
373
- const ts = lastTimestamp ? new Date(lastTimestamp) : new Date();
385
+ // No valid timestamp skip instead of stamping "now": this parser is
386
+ // stateless, so a "now" fallback would re-key the same record into a
387
+ // fresh 30-min bucket on every sync (duplicates).
388
+ if (!lastTimestamp) continue;
389
+ const ts = new Date(lastTimestamp);
390
+ if (isNaN(ts.getTime())) continue;
374
391
 
375
392
  entries.push({
376
393
  source: 'kimi-code',
377
394
  model: currentModel,
378
395
  project,
379
396
  timestamp: ts,
380
- inputTokens: tokenUsage.input_other || 0,
397
+ // Cache creation is billed non-cached input, matching the current
398
+ // parser and the common bucket model used by the other parsers.
399
+ inputTokens: (tokenUsage.input_other || 0) + (tokenUsage.input_cache_creation || 0),
381
400
  outputTokens: tokenUsage.output || 0,
382
401
  cachedInputTokens: tokenUsage.input_cache_read || 0,
383
402
  reasoningOutputTokens: 0,
@@ -389,9 +408,13 @@ function parseLegacyKimi() {
389
408
  }
390
409
 
391
410
  export async function parse() {
392
- // Prefer the current ~/.kimi-code layout; fall back to legacy ~/.kimi only
393
- // when no kimi-code sessions exist, so migrated users aren't double-counted.
411
+ // Always parse both stores and merge (see the header comment): legacy usage
412
+ // is never carried into ~/.kimi-code by `kimi migrate`, so a migrated user's
413
+ // history exists only in ~/.kimi.
394
414
  const current = parseKimiCode();
395
- if (current) return current;
396
- return parseLegacyKimi();
415
+ const legacy = parseLegacyKimi();
416
+ return {
417
+ buckets: [...(current?.buckets ?? []), ...legacy.buckets],
418
+ sessions: [...(current?.sessions ?? []), ...legacy.sessions],
419
+ };
397
420
  }