@vibe-cafe/vibe-usage 0.7.18 → 0.7.19

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
@@ -47,7 +47,7 @@ npx @vibe-cafe/vibe-usage status # Show config & detected tools
47
47
  | Tool | Data Location |
48
48
  |------|---------------|
49
49
  | Claude Code | `~/.claude/projects/` (tokens + sessions), `~/.claude/transcripts/` (sessions only) |
50
- | Codex CLI | `~/.codex/sessions/` |
50
+ | Codex CLI | `~/.codex/sessions/` and `~/.codex/archived_sessions/` |
51
51
  | GitHub Copilot CLI | `~/.copilot/session-state/*/events.jsonl` |
52
52
  | Cursor | `state.vscdb` (SQLite, reads `cursorAuth/accessToken`, fetches CSV from `cursor.com`) |
53
53
  | Gemini CLI | `~/.gemini/tmp/` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibe-cafe/vibe-usage",
3
- "version": "0.7.18",
3
+ "version": "0.7.19",
4
4
  "description": "Track your AI coding tool token usage and sync to vibecafe.ai",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,7 +4,18 @@ import { homedir } from 'node:os';
4
4
  import { createInterface } from 'node:readline';
5
5
  import { aggregateToBuckets, extractSessions } from './index.js';
6
6
 
7
- const SESSIONS_DIR = join(homedir(), '.codex', 'sessions');
7
+ // Codex stores live sessions in ~/.codex/sessions and, once a session is
8
+ // "completed", moves its rollout file verbatim into ~/.codex/archived_sessions.
9
+ // A session can be archived between two syncs, so scanning only the live dir
10
+ // loses that session's usage forever. We scan both: the parser is stateless
11
+ // and the server dedups on (source, sessionHash/bucket), so re-reading an
12
+ // archived file that was already synced from sessions/ is idempotent. Indexing
13
+ // both together also keeps fork replay-skip correct when a fork and its parent
14
+ // end up split across the two directories.
15
+ const SESSIONS_DIRS = [
16
+ join(homedir(), '.codex', 'sessions'),
17
+ join(homedir(), '.codex', 'archived_sessions'),
18
+ ];
8
19
 
9
20
  /**
10
21
  * Recursively find all .jsonl files under a directory.
@@ -80,11 +91,11 @@ async function indexSessionFile(filePath) {
80
91
  }
81
92
 
82
93
  export async function parse() {
83
- if (!existsSync(SESSIONS_DIR)) return { buckets: [], sessions: [] };
94
+ if (!SESSIONS_DIRS.some(existsSync)) return { buckets: [], sessions: [] };
84
95
 
85
96
  const entries = [];
86
97
  const sessionEvents = [];
87
- const files = findJsonlFiles(SESSIONS_DIR);
98
+ const files = SESSIONS_DIRS.flatMap(findJsonlFiles);
88
99
  if (files.length === 0) return { buckets: [], sessions: [] };
89
100
 
90
101
  // Pass 1: index every session by its UUID and count its token_count
@@ -134,6 +145,12 @@ export async function parse() {
134
145
  let tokenCountSeen = 0;
135
146
 
136
147
  const sessionProject = fm.sessionProject;
148
+ // Group timing events by the real Codex session id, not the file path: the
149
+ // same session can briefly exist in both sessions/ and archived_sessions/
150
+ // (mid-archive, or a re-synced archive). Path-keyed grouping would emit it
151
+ // as two different sessionHashes and double-count its session stats. Fall
152
+ // back to the path only when the id is unknown (corrupt/missing meta).
153
+ const sessionKey = fm.sessionId || filePath;
137
154
 
138
155
  let turnContextModel = 'unknown';
139
156
  const prevTotal = new Map();
@@ -161,7 +178,7 @@ export async function parse() {
161
178
  if (!isReplay) {
162
179
  const isUserTurn = obj.type === 'turn_context' || obj.type === 'session_meta';
163
180
  sessionEvents.push({
164
- sessionId: filePath,
181
+ sessionId: sessionKey,
165
182
  source: 'codex',
166
183
  project: sessionProject,
167
184
  timestamp: evTs,
package/src/tools.js CHANGED
@@ -80,6 +80,16 @@ function findOpenclawDataDirs() {
80
80
  return dirs;
81
81
  }
82
82
 
83
+ // Codex keeps live sessions in ~/.codex/sessions and moves completed ones to
84
+ // ~/.codex/archived_sessions. Detect Codex if either dir exists, so a user
85
+ // whose sessions have all been archived is still recognized.
86
+ function findCodexDataDirs() {
87
+ return [
88
+ join(homedir(), '.codex', 'sessions'),
89
+ join(homedir(), '.codex', 'archived_sessions'),
90
+ ].filter(existsSync);
91
+ }
92
+
83
93
  export const TOOLS = [
84
94
  {
85
95
  name: 'Antigravity',
@@ -101,6 +111,7 @@ export const TOOLS = [
101
111
  name: 'Codex CLI',
102
112
  id: 'codex',
103
113
  dataDir: join(homedir(), '.codex', 'sessions'),
114
+ detectDataDirs: findCodexDataDirs,
104
115
  },
105
116
  {
106
117
  name: 'GitHub Copilot CLI',