costhawk 1.5.10 → 1.5.12

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.
@@ -1,2 +1,2 @@
1
- export declare const BUILD_COMMIT_SHA = "2ee9745";
1
+ export declare const BUILD_COMMIT_SHA = "d2dc17c";
2
2
  //# sourceMappingURL=build-info.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Auto-generated during release builds. Values may be empty in dev.
2
- export const BUILD_COMMIT_SHA = "2ee9745";
2
+ export const BUILD_COMMIT_SHA = "d2dc17c";
3
3
  //# sourceMappingURL=build-info.js.map
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Cursor Local SQLite Parser (PR1 — dry-run only)
3
+ *
4
+ * Parses Cursor IDE chat history from the local SQLite database to extract
5
+ * token usage data. Read-only. Does not push to any backend.
6
+ *
7
+ * Storage:
8
+ * macOS: ~/Library/Application Support/Cursor/User/globalStorage/state.vscdb
9
+ * Linux: ~/.config/Cursor/User/globalStorage/state.vscdb
10
+ * Windows: %APPDATA%/Cursor/User/globalStorage/state.vscdb
11
+ *
12
+ * Schema:
13
+ * Table cursorDiskKV (key TEXT, value BLOB)
14
+ * Conversations: composerData:<composerId>
15
+ * Messages: bubbleId:<composerId>:<bubbleId>
16
+ *
17
+ * Token data lives at $.tokenCount.inputTokens and $.tokenCount.outputTokens
18
+ * on bubble rows. Model name at $.modelInfo.modelName. Server-side dedup id
19
+ * at $.serverBubbleId.
20
+ *
21
+ * NOTE (PR1 scope): Cursor message timestamps are not yet verified across
22
+ * versions, so this dry-run parser does NOT return startTime/endTime/dailyUsage.
23
+ * PR2 will add timestamp support after verification on real Cursor data.
24
+ *
25
+ * Workspace metadata fields (workspaceHash/workspaceName) are also unverified
26
+ * and return null until PR2 confirms the stable field source.
27
+ */
28
+ import type { TokenUsage } from "./transcript-parser.js";
29
+ /**
30
+ * Single Cursor session in dry-run output.
31
+ *
32
+ * Shape is intentionally distinct from Claude/Codex SessionUsage because
33
+ * Cursor message timestamps are not yet verified — startTime/endTime are
34
+ * deliberately absent until PR2 verifies the stable timestamp source.
35
+ */
36
+ export interface CursorSessionUsageDryRun {
37
+ sessionId: string;
38
+ workspaceHash: string | null;
39
+ workspaceName: string | null;
40
+ model: string;
41
+ tokens: TokenUsage;
42
+ messageCount: number;
43
+ filePath: string;
44
+ }
45
+ export interface CursorParserError {
46
+ code: "CURSOR_DB_NOT_FOUND" | "CURSOR_SQLITE3_NOT_FOUND" | "CURSOR_SQLITE_QUERY_FAILED";
47
+ message: string;
48
+ }
49
+ export interface CursorParserResult {
50
+ sessions: CursorSessionUsageDryRun[];
51
+ filePath: string;
52
+ }
53
+ /**
54
+ * Get the default Cursor SQLite path for the current platform, honoring
55
+ * the COSTHAWK_CURSOR_DB_PATH environment override.
56
+ */
57
+ export declare function getCursorDbPath(): string;
58
+ /**
59
+ * Check whether the Cursor SQLite database exists at the resolved path.
60
+ */
61
+ export declare function cursorDbExists(): boolean;
62
+ /**
63
+ * Type guard — narrows an unknown error to a CursorParserError.
64
+ */
65
+ declare function isCursorParserError(value: unknown): value is CursorParserError;
66
+ /**
67
+ * Parse Cursor usage from local SQLite. Read-only dry run — does NOT push
68
+ * anything to the costcanary backend.
69
+ *
70
+ * Returns aggregated session data per composer with per-session token totals
71
+ * and message counts. Throws CursorParserError on unrecoverable failures
72
+ * (missing DB, missing sqlite3 binary, malformed SQLite output).
73
+ *
74
+ * Dedup strategy: per composer, keep one entry per (serverBubbleId ?? bubbleId).
75
+ * On collision, keep the candidate with the larger token total.
76
+ *
77
+ * Mixed-model handling: if a composer contains multiple non-empty model names,
78
+ * the returned `model` field is "mixed". If no model info is present on any
79
+ * bubble, the field is "unknown".
80
+ *
81
+ * Sort order: total tokens descending. NOT chronological — message timestamps
82
+ * are not yet verified for Cursor.
83
+ */
84
+ export declare function parseCursorUsageDryRun(): CursorParserResult;
85
+ export { isCursorParserError };
86
+ //# sourceMappingURL=cursor-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-parser.d.ts","sourceRoot":"","sources":["../src/cursor-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAOzD;;;;;;GAMG;AACH,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EACA,qBAAqB,GACrB,0BAA0B,GAC1B,4BAA4B,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,wBAAwB,EAAE,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAmCxC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAUD;;GAEG;AACH,iBAAS,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAYvE;AA+KD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,IAAI,kBAAkB,CAuH3D;AAID,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Cursor Local SQLite Parser (PR1 — dry-run only)
3
+ *
4
+ * Parses Cursor IDE chat history from the local SQLite database to extract
5
+ * token usage data. Read-only. Does not push to any backend.
6
+ *
7
+ * Storage:
8
+ * macOS: ~/Library/Application Support/Cursor/User/globalStorage/state.vscdb
9
+ * Linux: ~/.config/Cursor/User/globalStorage/state.vscdb
10
+ * Windows: %APPDATA%/Cursor/User/globalStorage/state.vscdb
11
+ *
12
+ * Schema:
13
+ * Table cursorDiskKV (key TEXT, value BLOB)
14
+ * Conversations: composerData:<composerId>
15
+ * Messages: bubbleId:<composerId>:<bubbleId>
16
+ *
17
+ * Token data lives at $.tokenCount.inputTokens and $.tokenCount.outputTokens
18
+ * on bubble rows. Model name at $.modelInfo.modelName. Server-side dedup id
19
+ * at $.serverBubbleId.
20
+ *
21
+ * NOTE (PR1 scope): Cursor message timestamps are not yet verified across
22
+ * versions, so this dry-run parser does NOT return startTime/endTime/dailyUsage.
23
+ * PR2 will add timestamp support after verification on real Cursor data.
24
+ *
25
+ * Workspace metadata fields (workspaceHash/workspaceName) are also unverified
26
+ * and return null until PR2 confirms the stable field source.
27
+ */
28
+ import { execFileSync } from "child_process";
29
+ import { existsSync } from "fs";
30
+ import { homedir, platform } from "os";
31
+ import { join } from "path";
32
+ // Defaults — overridable via env vars
33
+ const DEFAULT_SQLITE3_PATH = "/usr/bin/sqlite3";
34
+ const SQLITE_TIMEOUT_MS = 10_000;
35
+ const SQLITE_MAX_BUFFER_BYTES = 32 * 1024 * 1024;
36
+ /**
37
+ * Get the default Cursor SQLite path for the current platform, honoring
38
+ * the COSTHAWK_CURSOR_DB_PATH environment override.
39
+ */
40
+ export function getCursorDbPath() {
41
+ const envOverride = process.env.COSTHAWK_CURSOR_DB_PATH;
42
+ if (envOverride && envOverride.length > 0) {
43
+ return envOverride;
44
+ }
45
+ const home = homedir();
46
+ if (platform() === "darwin") {
47
+ return join(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
48
+ }
49
+ if (platform() === "win32") {
50
+ const appData = process.env.APPDATA;
51
+ if (appData) {
52
+ return join(appData, "Cursor", "User", "globalStorage", "state.vscdb");
53
+ }
54
+ return join(home, "AppData", "Roaming", "Cursor", "User", "globalStorage", "state.vscdb");
55
+ }
56
+ // Linux and other unix-likes
57
+ return join(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
58
+ }
59
+ /**
60
+ * Check whether the Cursor SQLite database exists at the resolved path.
61
+ */
62
+ export function cursorDbExists() {
63
+ return existsSync(getCursorDbPath());
64
+ }
65
+ /**
66
+ * Resolve the sqlite3 binary path. Defaults to /usr/bin/sqlite3, honoring
67
+ * the COSTHAWK_SQLITE3_PATH environment override.
68
+ */
69
+ function getSqlite3Path() {
70
+ return process.env.COSTHAWK_SQLITE3_PATH ?? DEFAULT_SQLITE3_PATH;
71
+ }
72
+ /**
73
+ * Type guard — narrows an unknown error to a CursorParserError.
74
+ */
75
+ function isCursorParserError(value) {
76
+ if (typeof value !== "object" || value === null) {
77
+ return false;
78
+ }
79
+ const obj = value;
80
+ return (typeof obj.code === "string" &&
81
+ (obj.code === "CURSOR_DB_NOT_FOUND" ||
82
+ obj.code === "CURSOR_SQLITE3_NOT_FOUND" ||
83
+ obj.code === "CURSOR_SQLITE_QUERY_FAILED") &&
84
+ typeof obj.message === "string");
85
+ }
86
+ /**
87
+ * Run a SQL query against the Cursor SQLite via shell-out to the system
88
+ * sqlite3 binary. Returns parsed rows as an array of {key, value} objects,
89
+ * or throws CursorParserError on unrecoverable failures.
90
+ *
91
+ * Uses execFileSync with an arg array (not shell strings) to avoid shell
92
+ * injection. Sets explicit timeout and maxBuffer to defend against runaway
93
+ * queries or oversized state.vscdb files.
94
+ */
95
+ function runCursorQuery(sql) {
96
+ const sqlite3Path = getSqlite3Path();
97
+ const dbPath = getCursorDbPath();
98
+ if (!existsSync(dbPath)) {
99
+ const error = {
100
+ code: "CURSOR_DB_NOT_FOUND",
101
+ message: `Cursor SQLite database not found at ${dbPath}. Make sure Cursor is installed and you have used it at least once. Set COSTHAWK_CURSOR_DB_PATH to override.`,
102
+ };
103
+ throw error;
104
+ }
105
+ let stdout;
106
+ try {
107
+ stdout = execFileSync(sqlite3Path, ["-readonly", "-batch", "-json", "--", dbPath, sql], {
108
+ encoding: "utf8",
109
+ timeout: SQLITE_TIMEOUT_MS,
110
+ maxBuffer: SQLITE_MAX_BUFFER_BYTES,
111
+ });
112
+ }
113
+ catch (err) {
114
+ const errno = err.code;
115
+ if (errno === "ENOENT") {
116
+ const error = {
117
+ code: "CURSOR_SQLITE3_NOT_FOUND",
118
+ message: `sqlite3 binary not found at ${sqlite3Path}. Set COSTHAWK_SQLITE3_PATH to override the default path.`,
119
+ };
120
+ throw error;
121
+ }
122
+ const message = err instanceof Error ? err.message : String(err);
123
+ const error = {
124
+ code: "CURSOR_SQLITE_QUERY_FAILED",
125
+ message,
126
+ };
127
+ throw error;
128
+ }
129
+ if (!stdout || stdout.trim().length === 0) {
130
+ return [];
131
+ }
132
+ // sqlite3 -json output is a JSON array of objects when rows exist,
133
+ // or empty / whitespace when no rows. Anything else is a real failure
134
+ // and must surface as CURSOR_SQLITE_QUERY_FAILED — silently returning
135
+ // [] would mask a parser failure as "no sessions found".
136
+ let parsed;
137
+ try {
138
+ parsed = JSON.parse(stdout);
139
+ }
140
+ catch {
141
+ const error = {
142
+ code: "CURSOR_SQLITE_QUERY_FAILED",
143
+ message: "sqlite3 returned invalid JSON output",
144
+ };
145
+ throw error;
146
+ }
147
+ if (!Array.isArray(parsed)) {
148
+ const error = {
149
+ code: "CURSOR_SQLITE_QUERY_FAILED",
150
+ message: "sqlite3 returned a non-array JSON payload",
151
+ };
152
+ throw error;
153
+ }
154
+ return parsed.filter((row) => typeof row === "object" &&
155
+ row !== null &&
156
+ "key" in row &&
157
+ "value" in row &&
158
+ typeof row.key === "string" &&
159
+ typeof row.value === "string");
160
+ }
161
+ function hasTokenUsage(bubble) {
162
+ return bubble.inputTokens > 0 || bubble.outputTokens > 0;
163
+ }
164
+ const BUBBLE_KEY_REGEX = /^bubbleId:([^:]+):(.+)$/;
165
+ /**
166
+ * Parse a single bubbleId row into structured BubbleData.
167
+ *
168
+ * Returns null if the row key is malformed, the value is not parseable JSON,
169
+ * or the row contains neither a non-empty model name nor any positive token
170
+ * counts. Cursor can store model metadata and token usage on different rows
171
+ * (model name typically lives on user-prompt bubbles, token counts live on
172
+ * assistant-response bubbles), so the parser must accept either signal in
173
+ * isolation and let the per-composer aggregation merge them.
174
+ */
175
+ function parseBubble(row) {
176
+ const match = BUBBLE_KEY_REGEX.exec(row.key);
177
+ if (!match) {
178
+ return null;
179
+ }
180
+ const [, composerId, bubbleId] = match;
181
+ let value;
182
+ try {
183
+ value = JSON.parse(row.value);
184
+ }
185
+ catch {
186
+ return null;
187
+ }
188
+ if (typeof value !== "object" || value === null) {
189
+ return null;
190
+ }
191
+ const obj = value;
192
+ let inputTokens = 0;
193
+ let outputTokens = 0;
194
+ const tokenCount = obj.tokenCount;
195
+ if (typeof tokenCount === "object" && tokenCount !== null) {
196
+ const tc = tokenCount;
197
+ inputTokens = typeof tc.inputTokens === "number" ? tc.inputTokens : 0;
198
+ outputTokens = typeof tc.outputTokens === "number" ? tc.outputTokens : 0;
199
+ }
200
+ let modelName;
201
+ const modelInfo = obj.modelInfo;
202
+ if (typeof modelInfo === "object" && modelInfo !== null) {
203
+ const mi = modelInfo;
204
+ if (typeof mi.modelName === "string" && mi.modelName.length > 0) {
205
+ modelName = mi.modelName;
206
+ }
207
+ }
208
+ // Skip rows with no usable signal at all — neither model metadata nor
209
+ // positive token counts. These are typically system messages, empty
210
+ // bubbles, or tool-call bookkeeping rows.
211
+ if (!modelName && inputTokens === 0 && outputTokens === 0) {
212
+ return null;
213
+ }
214
+ let serverBubbleId;
215
+ if (typeof obj.serverBubbleId === "string" &&
216
+ obj.serverBubbleId.length > 0) {
217
+ serverBubbleId = obj.serverBubbleId;
218
+ }
219
+ return {
220
+ composerId,
221
+ bubbleId,
222
+ serverBubbleId,
223
+ modelName,
224
+ inputTokens,
225
+ outputTokens,
226
+ };
227
+ }
228
+ /**
229
+ * Parse Cursor usage from local SQLite. Read-only dry run — does NOT push
230
+ * anything to the costcanary backend.
231
+ *
232
+ * Returns aggregated session data per composer with per-session token totals
233
+ * and message counts. Throws CursorParserError on unrecoverable failures
234
+ * (missing DB, missing sqlite3 binary, malformed SQLite output).
235
+ *
236
+ * Dedup strategy: per composer, keep one entry per (serverBubbleId ?? bubbleId).
237
+ * On collision, keep the candidate with the larger token total.
238
+ *
239
+ * Mixed-model handling: if a composer contains multiple non-empty model names,
240
+ * the returned `model` field is "mixed". If no model info is present on any
241
+ * bubble, the field is "unknown".
242
+ *
243
+ * Sort order: total tokens descending. NOT chronological — message timestamps
244
+ * are not yet verified for Cursor.
245
+ */
246
+ export function parseCursorUsageDryRun() {
247
+ const dbPath = getCursorDbPath();
248
+ // Throws CursorParserError on missing DB / missing sqlite3 / query failure
249
+ const rows = runCursorQuery("SELECT key, value FROM cursorDiskKV WHERE key LIKE 'bubbleId:%'");
250
+ // Cursor splits model metadata and token usage across different bubble
251
+ // rows: model names typically live on user-prompt bubbles (type 1) with
252
+ // zero token counts, and token counts live on assistant-response bubbles
253
+ // (type 2) with no model info. We collect them separately and merge per
254
+ // composer.
255
+ //
256
+ // - tokenBubblesByComposer: per-composer dedup map for bubbles that carry
257
+ // positive token counts. Dedup key is (serverBubbleId ?? bubbleId).
258
+ // Collision rule: keep the candidate with the larger token total.
259
+ // - modelsByComposer: per-composer set of all distinct non-empty model
260
+ // names found on ANY bubble row in the composer. No dedup needed since
261
+ // models are categorical, not additive.
262
+ //
263
+ // Model harvesting is intentionally not gated on type or on token presence
264
+ // — if Cursor ever stores model names on assistant rows in a future
265
+ // schema, this code already supports it.
266
+ const tokenBubblesByComposer = new Map();
267
+ const modelsByComposer = new Map();
268
+ for (const row of rows) {
269
+ const bubble = parseBubble(row);
270
+ if (!bubble) {
271
+ continue;
272
+ }
273
+ if (bubble.modelName) {
274
+ let composerModels = modelsByComposer.get(bubble.composerId);
275
+ if (!composerModels) {
276
+ composerModels = new Set();
277
+ modelsByComposer.set(bubble.composerId, composerModels);
278
+ }
279
+ composerModels.add(bubble.modelName);
280
+ }
281
+ if (!hasTokenUsage(bubble)) {
282
+ continue;
283
+ }
284
+ let composerMap = tokenBubblesByComposer.get(bubble.composerId);
285
+ if (!composerMap) {
286
+ composerMap = new Map();
287
+ tokenBubblesByComposer.set(bubble.composerId, composerMap);
288
+ }
289
+ const dedupKey = bubble.serverBubbleId ?? bubble.bubbleId;
290
+ const existing = composerMap.get(dedupKey);
291
+ if (existing) {
292
+ const existingTotal = existing.inputTokens + existing.outputTokens;
293
+ const newTotal = bubble.inputTokens + bubble.outputTokens;
294
+ if (newTotal > existingTotal) {
295
+ composerMap.set(dedupKey, bubble);
296
+ }
297
+ continue;
298
+ }
299
+ composerMap.set(dedupKey, bubble);
300
+ }
301
+ // Aggregate per composer into the dry-run output shape.
302
+ const sessions = [];
303
+ for (const [composerId, composerMap] of tokenBubblesByComposer) {
304
+ let inputTokens = 0;
305
+ let outputTokens = 0;
306
+ let messageCount = 0;
307
+ const modelsSeen = modelsByComposer.get(composerId) ?? new Set();
308
+ for (const bubble of composerMap.values()) {
309
+ inputTokens += bubble.inputTokens;
310
+ outputTokens += bubble.outputTokens;
311
+ messageCount += 1;
312
+ }
313
+ if (messageCount === 0) {
314
+ continue;
315
+ }
316
+ let model;
317
+ if (modelsSeen.size === 0) {
318
+ model = "unknown";
319
+ }
320
+ else if (modelsSeen.size === 1) {
321
+ model = Array.from(modelsSeen)[0];
322
+ }
323
+ else {
324
+ model = "mixed";
325
+ }
326
+ sessions.push({
327
+ sessionId: composerId,
328
+ workspaceHash: null, // Unverified in PR1 — set in PR2
329
+ workspaceName: null, // Unverified in PR1 — set in PR2
330
+ model,
331
+ tokens: {
332
+ inputTokens,
333
+ outputTokens,
334
+ cacheCreationTokens: 0, // Cursor does not have prompt cache tokens
335
+ cacheReadTokens: 0,
336
+ },
337
+ messageCount,
338
+ filePath: dbPath,
339
+ });
340
+ }
341
+ // Sort by total tokens descending. NOT recency — no verified timestamps.
342
+ sessions.sort((a, b) => {
343
+ const aTotal = a.tokens.inputTokens + a.tokens.outputTokens;
344
+ const bTotal = b.tokens.inputTokens + b.tokens.outputTokens;
345
+ return bTotal - aTotal;
346
+ });
347
+ return {
348
+ sessions,
349
+ filePath: dbPath,
350
+ };
351
+ }
352
+ // Re-export the type guard so the MCP tool registration in index.ts can
353
+ // distinguish CursorParserError from generic Error in its catch block.
354
+ export { isCursorParserError };
355
+ //# sourceMappingURL=cursor-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-parser.js","sourceRoot":"","sources":["../src/cursor-parser.ts"],"names":[],"mappings":"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"}