costhawk 1.2.2 → 1.3.1

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
@@ -11,6 +11,7 @@ CostHawk helps teams track, analyze, and optimize their AI API spending across p
11
11
  **Key Features:**
12
12
  - Real-time usage tracking and cost analytics
13
13
  - **Claude Code local usage tracking** with auto-sync (NEW in v1.3.0)
14
+ - **Codex local usage tracking** with optional auto-sync (NEW in v1.3.1)
14
15
  - Savings analysis for flat-rate subscriptions (Claude Pro/Max, OpenAI Pro)
15
16
  - Budget alerts and anomaly detection
16
17
  - Webhook notifications (Slack, Discord, PagerDuty)
@@ -61,6 +62,18 @@ Use costhawk_get_local_claude_code_usage with subscriptionPlan="max_5x"
61
62
 
62
63
  This shows your token usage, costs at retail rates, and whether you're saving money vs your subscription.
63
64
 
65
+ ### Codex Local Tracking (Beta)
66
+
67
+ These tools parse your local Codex session logs from `~/.codex/sessions/` to track token usage by type.
68
+
69
+ **Optional Auto-Sync:** Set `COSTHAWK_CODEX_AUTO_SYNC=true` to sync Codex usage every 15 minutes while the MCP server is running (requires `COSTHAWK_API_KEY`).
70
+
71
+ | Tool | Description |
72
+ |------|-------------|
73
+ | `costhawk_sync_codex_usage` | Sync local Codex usage to CostHawk dashboard |
74
+ | `costhawk_get_local_codex_usage` | View local Codex usage offline with token breakdown |
75
+ | `costhawk_list_codex_sessions` | List available Codex sessions |
76
+
64
77
  ### Savings Analysis
65
78
 
66
79
  | Tool | Description |
@@ -102,6 +115,7 @@ The cache read savings are significant - CostHawk tracks all 4 types to give you
102
115
  |----------|----------|-------------|
103
116
  | `COSTHAWK_API_KEY` | Yes* | Your CostHawk API token |
104
117
  | `COSTHAWK_API_URL` | No | API base URL (defaults to https://costhawk.ai) |
118
+ | `COSTHAWK_CODEX_AUTO_SYNC` | No | Enable Codex auto-sync (set to `true`) |
105
119
 
106
120
  *Required for most tools. Local Claude Code tools work offline without an API key.
107
121
 
@@ -138,6 +152,14 @@ The cache read savings are significant - CostHawk tracks all 4 types to give you
138
152
 
139
153
  ## Changelog
140
154
 
155
+ ### v1.3.1 (February 2026)
156
+ **Codex Local Tracking (Beta)**
157
+
158
+ - **New:** `costhawk_sync_codex_usage` - Sync local Codex session usage to CostHawk
159
+ - **New:** `costhawk_get_local_codex_usage` - View Codex usage offline with token breakdown
160
+ - **New:** `costhawk_list_codex_sessions` - List available local Codex sessions
161
+ - **New:** Optional Codex auto-sync (`COSTHAWK_CODEX_AUTO_SYNC=true`)
162
+
141
163
  ### v1.2.1 (January 2026)
142
164
  **Auto-Sync** - Automatic background syncing
143
165
 
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Codex Session Parser
3
+ *
4
+ * Parses local Codex session logs from ~/.codex/sessions/
5
+ * to extract token usage data for cost tracking.
6
+ *
7
+ * Session files are JSONL format where each line is a separate event.
8
+ * Token usage is embedded in "event_msg" entries with type "token_count".
9
+ */
10
+ export interface TokenUsage {
11
+ inputTokens: number;
12
+ outputTokens: number;
13
+ cacheCreationTokens: number;
14
+ cacheReadTokens: number;
15
+ }
16
+ export interface CodexSessionUsage {
17
+ sessionId: string;
18
+ projectHash: string;
19
+ model: string;
20
+ tokens: TokenUsage;
21
+ messageCount: number;
22
+ startTime: string;
23
+ endTime: string;
24
+ filePath: string;
25
+ }
26
+ export interface CodexSessionUsageDetailed extends CodexSessionUsage {
27
+ dailyUsage: Record<string, TokenUsage>;
28
+ rangeUsage?: TokenUsage;
29
+ }
30
+ export interface CodexSessionFile {
31
+ path: string;
32
+ sessionId: string;
33
+ lastModified: Date;
34
+ size: number;
35
+ }
36
+ export declare function getCodexSessionsDir(): string;
37
+ export declare function codexDirectoryExists(): boolean;
38
+ export declare function discoverCodexSessions(maxAgeHours?: number): CodexSessionFile[];
39
+ export declare function parseCodexSessionDetailed(filePath: string, options?: {
40
+ rangeStart?: Date;
41
+ rangeEnd?: Date;
42
+ }): CodexSessionUsageDetailed | null;
43
+ export declare function parseAllCodexSessionsDetailed(maxAgeHours?: number, options?: {
44
+ rangeStart?: Date;
45
+ rangeEnd?: Date;
46
+ }): CodexSessionUsageDetailed[];
47
+ //# sourceMappingURL=codex-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-parser.d.ts","sourceRoot":"","sources":["../src/codex-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,yBAA0B,SAAQ,iBAAiB;IAClE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACvC,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,IAAI,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAoKD,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,wBAAgB,qBAAqB,CAAC,WAAW,GAAE,MAAW,GAAG,gBAAgB,EAAE,CA8BlF;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;CAAE,GAC/C,yBAAyB,GAAG,IAAI,CAsHlC;AAED,wBAAgB,6BAA6B,CAC3C,WAAW,GAAE,MAAW,EACxB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;CAAE,GAC/C,yBAAyB,EAAE,CAY7B"}
@@ -0,0 +1,275 @@
1
+ /**
2
+ * Codex Session Parser
3
+ *
4
+ * Parses local Codex session logs from ~/.codex/sessions/
5
+ * to extract token usage data for cost tracking.
6
+ *
7
+ * Session files are JSONL format where each line is a separate event.
8
+ * Token usage is embedded in "event_msg" entries with type "token_count".
9
+ */
10
+ import { readdirSync, readFileSync, statSync, existsSync } from "fs";
11
+ import { join } from "path";
12
+ import { homedir } from "os";
13
+ import { createHash } from "crypto";
14
+ const CODEX_SESSIONS_DIR = join(homedir(), ".codex", "sessions");
15
+ const CODEX_CONFIG_PATH = join(homedir(), ".codex", "config.toml");
16
+ let cachedModel;
17
+ function createTokenUsage() {
18
+ return {
19
+ inputTokens: 0,
20
+ outputTokens: 0,
21
+ cacheCreationTokens: 0,
22
+ cacheReadTokens: 0,
23
+ };
24
+ }
25
+ function addTokenUsage(target, usage) {
26
+ target.inputTokens += usage.inputTokens;
27
+ target.outputTokens += usage.outputTokens;
28
+ target.cacheCreationTokens += usage.cacheCreationTokens;
29
+ target.cacheReadTokens += usage.cacheReadTokens;
30
+ }
31
+ function getDateKey(timestamp) {
32
+ const date = new Date(timestamp);
33
+ if (Number.isNaN(date.getTime()))
34
+ return null;
35
+ return date.toISOString().split("T")[0];
36
+ }
37
+ function normalizeTotals(totals) {
38
+ return {
39
+ input_tokens: totals?.input_tokens || 0,
40
+ output_tokens: totals?.output_tokens || 0,
41
+ cached_input_tokens: totals?.cached_input_tokens || 0,
42
+ reasoning_output_tokens: totals?.reasoning_output_tokens || 0,
43
+ };
44
+ }
45
+ function addTotals(a, b) {
46
+ return {
47
+ input_tokens: a.input_tokens + b.input_tokens,
48
+ output_tokens: a.output_tokens + b.output_tokens,
49
+ cached_input_tokens: a.cached_input_tokens + b.cached_input_tokens,
50
+ reasoning_output_tokens: a.reasoning_output_tokens + b.reasoning_output_tokens,
51
+ };
52
+ }
53
+ function subtractTotals(current, previous) {
54
+ return {
55
+ input_tokens: Math.max(0, current.input_tokens - previous.input_tokens),
56
+ output_tokens: Math.max(0, current.output_tokens - previous.output_tokens),
57
+ cached_input_tokens: Math.max(0, current.cached_input_tokens - previous.cached_input_tokens),
58
+ reasoning_output_tokens: Math.max(0, current.reasoning_output_tokens - previous.reasoning_output_tokens),
59
+ };
60
+ }
61
+ function getTokenDelta(info, lastTotals) {
62
+ const totalUsage = info?.total_token_usage;
63
+ if (totalUsage) {
64
+ const currentTotals = normalizeTotals(totalUsage);
65
+ const delta = lastTotals ? subtractTotals(currentTotals, lastTotals) : currentTotals;
66
+ return { delta, totals: currentTotals };
67
+ }
68
+ const lastUsage = info?.last_token_usage;
69
+ if (lastUsage) {
70
+ const delta = normalizeTotals(lastUsage);
71
+ const totals = lastTotals ? addTotals(lastTotals, delta) : delta;
72
+ return { delta, totals };
73
+ }
74
+ return null;
75
+ }
76
+ function getCodexModel() {
77
+ if (cachedModel !== undefined) {
78
+ return cachedModel;
79
+ }
80
+ if (!existsSync(CODEX_CONFIG_PATH)) {
81
+ cachedModel = null;
82
+ return cachedModel;
83
+ }
84
+ try {
85
+ const content = readFileSync(CODEX_CONFIG_PATH, "utf-8");
86
+ const match = content.match(/^\s*model\s*=\s*["']([^"']+)["']/m);
87
+ cachedModel = match ? match[1] : null;
88
+ return cachedModel;
89
+ }
90
+ catch {
91
+ cachedModel = null;
92
+ return cachedModel;
93
+ }
94
+ }
95
+ function hashProject(value) {
96
+ if (!value)
97
+ return "unknown";
98
+ return createHash("sha256").update(value).digest("hex").slice(0, 12);
99
+ }
100
+ function extractSessionIdFromFilename(fileName) {
101
+ const match = fileName.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);
102
+ if (match)
103
+ return match[0];
104
+ return fileName.replace(".jsonl", "");
105
+ }
106
+ function listJsonlFiles(dir) {
107
+ const files = [];
108
+ let entries;
109
+ try {
110
+ entries = readdirSync(dir, { withFileTypes: true });
111
+ }
112
+ catch {
113
+ return files;
114
+ }
115
+ for (const entry of entries) {
116
+ const fullPath = join(dir, entry.name);
117
+ if (entry.isDirectory()) {
118
+ files.push(...listJsonlFiles(fullPath));
119
+ }
120
+ else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
121
+ files.push(fullPath);
122
+ }
123
+ }
124
+ return files;
125
+ }
126
+ export function getCodexSessionsDir() {
127
+ return CODEX_SESSIONS_DIR;
128
+ }
129
+ export function codexDirectoryExists() {
130
+ return existsSync(CODEX_SESSIONS_DIR);
131
+ }
132
+ export function discoverCodexSessions(maxAgeHours = 24) {
133
+ const cutoffTime = Date.now() - maxAgeHours * 60 * 60 * 1000;
134
+ if (!codexDirectoryExists()) {
135
+ return [];
136
+ }
137
+ const jsonlFiles = listJsonlFiles(CODEX_SESSIONS_DIR);
138
+ const sessions = [];
139
+ for (const filePath of jsonlFiles) {
140
+ try {
141
+ const fileStat = statSync(filePath);
142
+ if (fileStat.mtimeMs < cutoffTime)
143
+ continue;
144
+ const fileName = filePath.split("/").pop() || filePath;
145
+ const sessionId = extractSessionIdFromFilename(fileName);
146
+ sessions.push({
147
+ path: filePath,
148
+ sessionId,
149
+ lastModified: new Date(fileStat.mtimeMs),
150
+ size: fileStat.size,
151
+ });
152
+ }
153
+ catch {
154
+ continue;
155
+ }
156
+ }
157
+ return sessions.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
158
+ }
159
+ export function parseCodexSessionDetailed(filePath, options) {
160
+ if (!existsSync(filePath)) {
161
+ return null;
162
+ }
163
+ try {
164
+ const content = readFileSync(filePath, "utf-8");
165
+ const lines = content.split("\n").filter((line) => line.trim());
166
+ const stats = createTokenUsage();
167
+ const dailyUsage = {};
168
+ const rangeUsage = options?.rangeStart && options?.rangeEnd ? createTokenUsage() : undefined;
169
+ let messageCount = 0;
170
+ let startTime = null;
171
+ let endTime = null;
172
+ let lastTimestamp = null;
173
+ let lastTotals = null;
174
+ let sessionId = extractSessionIdFromFilename(filePath.split("/").pop() || filePath);
175
+ let projectHash = "unknown";
176
+ let modelProvider = null;
177
+ let cwd = null;
178
+ for (const line of lines) {
179
+ try {
180
+ const entry = JSON.parse(line);
181
+ if (entry.timestamp) {
182
+ if (!startTime) {
183
+ startTime = entry.timestamp;
184
+ }
185
+ endTime = entry.timestamp;
186
+ lastTimestamp = entry.timestamp;
187
+ }
188
+ if (entry.type === "session_meta") {
189
+ if (entry.payload?.id)
190
+ sessionId = entry.payload.id;
191
+ if (entry.payload?.cwd)
192
+ cwd = entry.payload.cwd;
193
+ if (entry.payload?.model_provider)
194
+ modelProvider = entry.payload.model_provider;
195
+ }
196
+ if (entry.type === "event_msg" && entry.payload?.type === "token_count") {
197
+ const deltaResult = getTokenDelta(entry.payload.info, lastTotals);
198
+ if (!deltaResult)
199
+ continue;
200
+ lastTotals = deltaResult.totals;
201
+ const deltaTotal = deltaResult.delta.input_tokens +
202
+ deltaResult.delta.output_tokens +
203
+ deltaResult.delta.cached_input_tokens +
204
+ deltaResult.delta.reasoning_output_tokens;
205
+ if (deltaTotal === 0)
206
+ continue;
207
+ const usageTokens = {
208
+ inputTokens: deltaResult.delta.input_tokens,
209
+ outputTokens: deltaResult.delta.output_tokens + deltaResult.delta.reasoning_output_tokens,
210
+ cacheCreationTokens: 0,
211
+ cacheReadTokens: deltaResult.delta.cached_input_tokens,
212
+ };
213
+ addTokenUsage(stats, usageTokens);
214
+ messageCount++;
215
+ const usageTimestamp = entry.timestamp || lastTimestamp;
216
+ if (usageTimestamp) {
217
+ const dateKey = getDateKey(usageTimestamp);
218
+ if (dateKey) {
219
+ if (!dailyUsage[dateKey]) {
220
+ dailyUsage[dateKey] = createTokenUsage();
221
+ }
222
+ addTokenUsage(dailyUsage[dateKey], usageTokens);
223
+ }
224
+ if (rangeUsage && options?.rangeStart && options?.rangeEnd) {
225
+ const usageDate = new Date(usageTimestamp);
226
+ if (usageDate >= options.rangeStart && usageDate <= options.rangeEnd) {
227
+ addTokenUsage(rangeUsage, usageTokens);
228
+ }
229
+ }
230
+ }
231
+ }
232
+ }
233
+ catch {
234
+ continue;
235
+ }
236
+ }
237
+ if (messageCount === 0) {
238
+ return null;
239
+ }
240
+ projectHash = hashProject(cwd);
241
+ const modelFromConfig = getCodexModel();
242
+ const model = modelFromConfig
243
+ ? modelProvider
244
+ ? `${modelProvider}:${modelFromConfig}`
245
+ : modelFromConfig
246
+ : modelProvider || "codex";
247
+ return {
248
+ sessionId,
249
+ projectHash,
250
+ model,
251
+ tokens: stats,
252
+ messageCount,
253
+ startTime: startTime || new Date().toISOString(),
254
+ endTime: endTime || new Date().toISOString(),
255
+ filePath,
256
+ dailyUsage,
257
+ rangeUsage,
258
+ };
259
+ }
260
+ catch {
261
+ return null;
262
+ }
263
+ }
264
+ export function parseAllCodexSessionsDetailed(maxAgeHours = 24, options) {
265
+ const transcripts = discoverCodexSessions(maxAgeHours);
266
+ const sessions = [];
267
+ for (const transcript of transcripts) {
268
+ const session = parseCodexSessionDetailed(transcript.path, options);
269
+ if (session) {
270
+ sessions.push(session);
271
+ }
272
+ }
273
+ return sessions;
274
+ }
275
+ //# sourceMappingURL=codex-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-parser.js","sourceRoot":"","sources":["../src/codex-parser.ts"],"names":[],"mappings":"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"}