@aixle/insights 0.1.1 → 0.2.1-staging
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 +154 -3
- package/dist/auth/credentials.d.ts +7 -1
- package/dist/auth/credentials.js +71 -14
- package/dist/auth/exchange.d.ts +1 -1
- package/dist/auth/exchange.js +1 -1
- package/dist/auth/flow.d.ts +10 -1
- package/dist/auth/flow.js +38 -5
- package/dist/auth/keycloak.d.ts +1 -1
- package/dist/auth/keycloak.js +20 -1
- package/dist/cli.d.ts +7 -3
- package/dist/cli.js +87 -21
- package/dist/collect-cursor-payloads.d.ts +4 -3
- package/dist/collect-cursor-payloads.js +8 -5
- package/dist/cursor-checkpoints.d.ts +2 -2
- package/dist/cursor-payload-contract.d.ts +5 -5
- package/dist/cursor-payload-contract.js +7 -0
- package/dist/cursor-settings.d.ts +9 -4
- package/dist/cursor-settings.js +80 -10
- package/dist/cursor-store-audit.d.ts +2 -2
- package/dist/cursor-store-audit.js +22 -11
- package/dist/daily-stats-versions.d.ts +3 -1
- package/dist/daily-stats-versions.js +6 -7
- package/dist/health.d.ts +3 -1
- package/dist/health.js +13 -1
- package/dist/hooks/cursor-hooks-mapper.d.ts +3 -3
- package/dist/hooks/cursor-hooks-mapper.js +1 -1
- package/dist/hooks/cursor-hooks-reader.d.ts +2 -0
- package/dist/hooks/cursor-hooks-reader.js +2 -2
- package/dist/install/cursor.d.ts +34 -0
- package/dist/install/cursor.js +193 -0
- package/dist/install/index.d.ts +6 -4
- package/dist/install/index.js +6 -1
- package/dist/lib/client.d.ts +7 -0
- package/dist/lib/client.js +17 -0
- package/dist/lib/config.js +7 -2
- package/dist/lib/project-resolver.d.ts +5 -4
- package/dist/lib/project-resolver.js +20 -8
- package/dist/lib/transport-security.d.ts +13 -0
- package/dist/lib/transport-security.js +47 -0
- package/dist/pricing.d.ts +9 -1
- package/dist/pricing.js +39 -8
- package/dist/readers/claude.d.ts +54 -6
- package/dist/readers/claude.js +158 -6
- package/dist/readers/cursor-sqlite.d.ts +23 -0
- package/dist/readers/cursor-sqlite.js +68 -0
- package/dist/readers/cursor.d.ts +11 -8
- package/dist/readers/cursor.js +149 -31
- package/dist/server.d.ts +20 -3
- package/dist/server.js +101 -67
- package/dist/state.js +7 -2
- package/dist/sync.d.ts +4 -2
- package/dist/sync.js +61 -46
- package/package.json +2 -2
package/dist/readers/cursor.js
CHANGED
|
@@ -10,8 +10,8 @@ import { basename, dirname, join } from "node:path";
|
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { glob } from "glob";
|
|
13
|
-
import Database from "better-sqlite3";
|
|
14
13
|
import { scanText } from "../risk-scanner.js";
|
|
14
|
+
import { openCursorSqliteReadonly } from "./cursor-sqlite.js";
|
|
15
15
|
// ─── Reader: paths & SQLite ──────────────────────────────────────────────────
|
|
16
16
|
export function cursorUserDir() {
|
|
17
17
|
switch (process.platform) {
|
|
@@ -43,14 +43,19 @@ function logDbTables(db, dbPath, label) {
|
|
|
43
43
|
console.log(` tables: ${tables.join(", ") || "(none)"}`);
|
|
44
44
|
}
|
|
45
45
|
/** Smoke-test better-sqlite3 against the global Cursor state DB (CUR-V02 / verify scripts). */
|
|
46
|
-
export function probeCursorGlobalStateDb(verbose = false) {
|
|
47
|
-
const
|
|
46
|
+
export function probeCursorGlobalStateDb(verbose = false, baseDir) {
|
|
47
|
+
const userDir = baseDir ?? cursorUserDir();
|
|
48
|
+
const dbPath = join(userDir, "globalStorage", "state.vscdb");
|
|
49
|
+
const opened = openCursorSqliteReadonly(dbPath, { rootDir: userDir });
|
|
50
|
+
if (!opened.ok) {
|
|
51
|
+
console.error(` [probe] failed to read ${dbPath}: ${opened.message}`);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const db = opened.db;
|
|
48
55
|
try {
|
|
49
|
-
const db = new Database(dbPath, { readonly: true });
|
|
50
56
|
const row = db
|
|
51
57
|
.prepare(`SELECT count(*) AS c FROM ${STATE_TABLE} WHERE key LIKE 'aiCodeTracking.dailyStats%'`)
|
|
52
58
|
.get();
|
|
53
|
-
db.close();
|
|
54
59
|
if (verbose) {
|
|
55
60
|
console.log(` [probe] global state.vscdb OK — ${row.c} dailyStats key(s)`);
|
|
56
61
|
}
|
|
@@ -61,6 +66,9 @@ export function probeCursorGlobalStateDb(verbose = false) {
|
|
|
61
66
|
console.error(` [probe] failed to read ${dbPath}: ${msg}`);
|
|
62
67
|
return false;
|
|
63
68
|
}
|
|
69
|
+
finally {
|
|
70
|
+
db.close();
|
|
71
|
+
}
|
|
64
72
|
}
|
|
65
73
|
// ─── Legacy: cursor.db / CursorRequestFeedback ─────────────────────────────────
|
|
66
74
|
export function findCursorDbs(baseDir) {
|
|
@@ -72,10 +80,13 @@ export function findCursorDbs(baseDir) {
|
|
|
72
80
|
return [];
|
|
73
81
|
}
|
|
74
82
|
}
|
|
75
|
-
function readLegacyFromDb(dbPath, since, workspacePath, verbose) {
|
|
83
|
+
function readLegacyFromDb(dbPath, since, workspacePath, rootDir, verbose) {
|
|
76
84
|
let db = null;
|
|
77
85
|
try {
|
|
78
|
-
|
|
86
|
+
const opened = openCursorSqliteReadonly(dbPath, { rootDir });
|
|
87
|
+
if (!opened.ok)
|
|
88
|
+
return [];
|
|
89
|
+
db = opened.db;
|
|
79
90
|
if (verbose)
|
|
80
91
|
logDbTables(db, dbPath, "cursor.db");
|
|
81
92
|
if (!tableExists(db, LEGACY_TABLE))
|
|
@@ -105,13 +116,14 @@ function readLegacyFromDb(dbPath, since, workspacePath, verbose) {
|
|
|
105
116
|
}
|
|
106
117
|
}
|
|
107
118
|
export function readLegacyEvents(since, baseDir, verbose = false) {
|
|
119
|
+
const rootDir = baseDir ?? cursorUserDir();
|
|
108
120
|
const dbPaths = findCursorDbs(baseDir);
|
|
109
121
|
if (verbose)
|
|
110
122
|
console.log(`Found ${dbPaths.length} legacy cursor.db file(s)`);
|
|
111
123
|
const results = [];
|
|
112
124
|
for (const dbPath of dbPaths) {
|
|
113
125
|
const workspacePath = dbPath.replace(/[\\/]cursor\.db$/, "");
|
|
114
|
-
for (const row of readLegacyFromDb(dbPath, since, workspacePath, verbose)) {
|
|
126
|
+
for (const row of readLegacyFromDb(dbPath, since, workspacePath, rootDir, verbose)) {
|
|
115
127
|
results.push({ row, workspacePath });
|
|
116
128
|
}
|
|
117
129
|
}
|
|
@@ -239,10 +251,13 @@ export function findStateVscDbs(baseDir) {
|
|
|
239
251
|
}
|
|
240
252
|
return results;
|
|
241
253
|
}
|
|
242
|
-
function readDailyStatsFromDb(dbPath, since, verbose) {
|
|
254
|
+
function readDailyStatsFromDb(dbPath, since, rootDir, verbose) {
|
|
243
255
|
let db = null;
|
|
244
256
|
try {
|
|
245
|
-
|
|
257
|
+
const opened = openCursorSqliteReadonly(dbPath, { rootDir });
|
|
258
|
+
if (!opened.ok)
|
|
259
|
+
return [];
|
|
260
|
+
db = opened.db;
|
|
246
261
|
if (verbose)
|
|
247
262
|
logDbTables(db, dbPath, "state.vscdb");
|
|
248
263
|
if (!tableExists(db, STATE_TABLE))
|
|
@@ -264,7 +279,7 @@ function readDailyStatsFromDb(dbPath, since, verbose) {
|
|
|
264
279
|
if (!dateMatch)
|
|
265
280
|
continue;
|
|
266
281
|
const date = dateMatch[1];
|
|
267
|
-
if (since && date
|
|
282
|
+
if (since && date < since.toISOString().slice(0, 10))
|
|
268
283
|
continue;
|
|
269
284
|
let parsed;
|
|
270
285
|
try {
|
|
@@ -285,6 +300,7 @@ function readDailyStatsFromDb(dbPath, since, verbose) {
|
|
|
285
300
|
}
|
|
286
301
|
}
|
|
287
302
|
function readDailyStatsRaw(since, baseDir, verbose) {
|
|
303
|
+
const rootDir = baseDir ?? cursorUserDir();
|
|
288
304
|
const dbPaths = findStateVscDbs(baseDir);
|
|
289
305
|
if (verbose) {
|
|
290
306
|
console.log(`Searching: ${baseDir ?? cursorUserDir()}`);
|
|
@@ -292,7 +308,7 @@ function readDailyStatsRaw(since, baseDir, verbose) {
|
|
|
292
308
|
}
|
|
293
309
|
const raw = [];
|
|
294
310
|
for (const dbPath of dbPaths) {
|
|
295
|
-
raw.push(...readDailyStatsFromDb(dbPath, since, verbose));
|
|
311
|
+
raw.push(...readDailyStatsFromDb(dbPath, since, rootDir, verbose));
|
|
296
312
|
}
|
|
297
313
|
return raw;
|
|
298
314
|
}
|
|
@@ -339,10 +355,13 @@ function dedupeRecentCommitSnapshots(entries) {
|
|
|
339
355
|
}
|
|
340
356
|
return [...byKey.values()];
|
|
341
357
|
}
|
|
342
|
-
function readRecentCommitFromDb(dbPath, since, verbose) {
|
|
358
|
+
function readRecentCommitFromDb(dbPath, since, rootDir, verbose) {
|
|
343
359
|
let db = null;
|
|
344
360
|
try {
|
|
345
|
-
|
|
361
|
+
const opened = openCursorSqliteReadonly(dbPath, { rootDir });
|
|
362
|
+
if (!opened.ok)
|
|
363
|
+
return [];
|
|
364
|
+
db = opened.db;
|
|
346
365
|
if (!tableExists(db, STATE_TABLE))
|
|
347
366
|
return [];
|
|
348
367
|
const row = db
|
|
@@ -384,13 +403,14 @@ function readRecentCommitFromDb(dbPath, since, verbose) {
|
|
|
384
403
|
}
|
|
385
404
|
}
|
|
386
405
|
export function readRecentCommitSnapshots(since, baseDir, verbose = false) {
|
|
406
|
+
const rootDir = baseDir ?? cursorUserDir();
|
|
387
407
|
const dbPaths = findStateVscDbs(baseDir);
|
|
388
408
|
if (verbose) {
|
|
389
409
|
console.log(`Searching recentCommit: ${baseDir ?? cursorUserDir()}`);
|
|
390
410
|
}
|
|
391
411
|
const found = [];
|
|
392
412
|
for (const dbPath of dbPaths) {
|
|
393
|
-
found.push(...readRecentCommitFromDb(dbPath, since, verbose));
|
|
413
|
+
found.push(...readRecentCommitFromDb(dbPath, since, rootDir, verbose));
|
|
394
414
|
}
|
|
395
415
|
return dedupeRecentCommitSnapshots(found);
|
|
396
416
|
}
|
|
@@ -427,12 +447,71 @@ function toIsoFromMs(value) {
|
|
|
427
447
|
const date = new Date(value);
|
|
428
448
|
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
429
449
|
}
|
|
450
|
+
/** Coerce epoch ms/seconds, numeric strings, or ISO-8601 into an ISO timestamp. */
|
|
451
|
+
function coerceTranscriptTimestamp(value) {
|
|
452
|
+
if (value == null)
|
|
453
|
+
return null;
|
|
454
|
+
if (typeof value === "number")
|
|
455
|
+
return toIsoFromMs(value);
|
|
456
|
+
if (typeof value !== "string")
|
|
457
|
+
return null;
|
|
458
|
+
const trimmed = value.trim();
|
|
459
|
+
if (!trimmed)
|
|
460
|
+
return null;
|
|
461
|
+
const fromEpoch = toIsoString(trimmed);
|
|
462
|
+
if (fromEpoch)
|
|
463
|
+
return fromEpoch;
|
|
464
|
+
const ms = Date.parse(trimmed);
|
|
465
|
+
if (Number.isNaN(ms))
|
|
466
|
+
return null;
|
|
467
|
+
return new Date(ms).toISOString();
|
|
468
|
+
}
|
|
469
|
+
/** Prefer explicit per-line / per-message times when Cursor includes them. */
|
|
470
|
+
function extractLineOccurredAt(entry) {
|
|
471
|
+
const candidates = [
|
|
472
|
+
entry.timestamp,
|
|
473
|
+
entry.createdAt,
|
|
474
|
+
entry.unixMs,
|
|
475
|
+
entry.message?.timestamp,
|
|
476
|
+
entry.message?.createdAt,
|
|
477
|
+
];
|
|
478
|
+
for (const candidate of candidates) {
|
|
479
|
+
const iso = coerceTranscriptTimestamp(candidate);
|
|
480
|
+
if (iso)
|
|
481
|
+
return iso;
|
|
482
|
+
}
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Spread turns across [start, end] when the JSONL has no per-message times.
|
|
487
|
+
* Prevents first-sync backfill from collapsing every turn onto lastUpdatedAt/mtime
|
|
488
|
+
* (DB90DV-605 weekly chart spike).
|
|
489
|
+
*/
|
|
490
|
+
function interpolateTurnOccurredAt(startIso, endIso, index, total, fallbackIso) {
|
|
491
|
+
if (total <= 0)
|
|
492
|
+
return fallbackIso;
|
|
493
|
+
// Single turn: prefer session end (lastUpdatedAt) — matches prior composer-header behavior.
|
|
494
|
+
if (total === 1)
|
|
495
|
+
return endIso ?? startIso ?? fallbackIso;
|
|
496
|
+
const startMs = startIso ? Date.parse(startIso) : NaN;
|
|
497
|
+
const endMs = endIso ? Date.parse(endIso) : NaN;
|
|
498
|
+
if (Number.isNaN(startMs) || Number.isNaN(endMs)) {
|
|
499
|
+
return startIso ?? endIso ?? fallbackIso;
|
|
500
|
+
}
|
|
501
|
+
if (endMs <= startMs)
|
|
502
|
+
return startIso ?? fallbackIso;
|
|
503
|
+
const ms = startMs + ((endMs - startMs) * index) / (total - 1);
|
|
504
|
+
return new Date(ms).toISOString();
|
|
505
|
+
}
|
|
430
506
|
function readComposerHeaders(baseDir) {
|
|
431
507
|
const userDir = baseDir ?? cursorUserDir();
|
|
432
508
|
const dbPath = join(userDir, "globalStorage", "state.vscdb");
|
|
433
509
|
let db = null;
|
|
434
510
|
try {
|
|
435
|
-
|
|
511
|
+
const opened = openCursorSqliteReadonly(dbPath, { rootDir: userDir });
|
|
512
|
+
if (!opened.ok)
|
|
513
|
+
return new Map();
|
|
514
|
+
db = opened.db;
|
|
436
515
|
if (!tableExists(db, STATE_TABLE))
|
|
437
516
|
return new Map();
|
|
438
517
|
const row = db
|
|
@@ -468,6 +547,7 @@ function readComposerHeaders(baseDir) {
|
|
|
468
547
|
composerId,
|
|
469
548
|
name: typeof composer.name === "string" ? composer.name : null,
|
|
470
549
|
workspacePath: typeof uri?.fsPath === "string" ? uri.fsPath : null,
|
|
550
|
+
createdAt: toIsoFromMs(composer.createdAt),
|
|
471
551
|
lastUpdatedAt: toIsoFromMs(composer.lastUpdatedAt),
|
|
472
552
|
});
|
|
473
553
|
}
|
|
@@ -540,11 +620,15 @@ function workspaceFromTranscriptFile(filePath) {
|
|
|
540
620
|
const MAX_TRANSCRIPT_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
541
621
|
export async function parseCursorTranscriptFile(filePath, composerHeaders, verbose = false) {
|
|
542
622
|
let fileSize = 0;
|
|
543
|
-
let
|
|
623
|
+
let fileMtimeIso = new Date().toISOString();
|
|
624
|
+
let fileBirthIso = null;
|
|
544
625
|
try {
|
|
545
626
|
const stat = statSync(filePath);
|
|
546
627
|
fileSize = stat.size;
|
|
547
|
-
|
|
628
|
+
fileMtimeIso = stat.mtime.toISOString();
|
|
629
|
+
if (stat.birthtime && !Number.isNaN(stat.birthtime.getTime()) && stat.birthtime.getTime() > 0) {
|
|
630
|
+
fileBirthIso = stat.birthtime.toISOString();
|
|
631
|
+
}
|
|
548
632
|
}
|
|
549
633
|
catch {
|
|
550
634
|
return [];
|
|
@@ -557,15 +641,25 @@ export async function parseCursorTranscriptFile(filePath, composerHeaders, verbo
|
|
|
557
641
|
}
|
|
558
642
|
const sessionId = basename(filePath, ".jsonl");
|
|
559
643
|
const header = composerHeaders.get(sessionId);
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
const
|
|
644
|
+
const sessionFallback = header?.lastUpdatedAt ?? fileMtimeIso;
|
|
645
|
+
const sessionStart = header?.createdAt ?? fileBirthIso ?? sessionFallback;
|
|
646
|
+
const sessionEnd = header?.lastUpdatedAt ?? fileMtimeIso;
|
|
647
|
+
const drafts = [];
|
|
563
648
|
let currentPromptParts = [];
|
|
564
649
|
let currentAssistantParts = [];
|
|
650
|
+
let currentTurnOccurredAt = null;
|
|
565
651
|
let turnIndex = 0;
|
|
566
652
|
const hasher = createHash("sha256");
|
|
567
653
|
const stream = createReadStream(filePath, { encoding: "utf-8" });
|
|
568
654
|
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
655
|
+
const noteLineTime = (entry) => {
|
|
656
|
+
const lineAt = extractLineOccurredAt(entry);
|
|
657
|
+
if (!lineAt)
|
|
658
|
+
return;
|
|
659
|
+
// Prefer the first timestamp in the turn (usually the user message).
|
|
660
|
+
if (!currentTurnOccurredAt)
|
|
661
|
+
currentTurnOccurredAt = lineAt;
|
|
662
|
+
};
|
|
569
663
|
const finalizeTurn = () => {
|
|
570
664
|
const promptText = currentPromptParts.join("\n\n").trim();
|
|
571
665
|
const assistantText = currentAssistantParts.join("\n\n").trim();
|
|
@@ -573,14 +667,14 @@ export async function parseCursorTranscriptFile(filePath, composerHeaders, verbo
|
|
|
573
667
|
return;
|
|
574
668
|
const risk = scanText(promptText);
|
|
575
669
|
turnIndex += 1;
|
|
576
|
-
|
|
670
|
+
drafts.push({
|
|
577
671
|
turnId: `${sessionId}:${turnIndex}`,
|
|
578
672
|
sessionId,
|
|
579
673
|
filePath,
|
|
580
674
|
fileSize,
|
|
581
675
|
workspacePath: header?.workspacePath ?? workspaceFromTranscriptFile(filePath),
|
|
582
676
|
composerName: header?.name ?? null,
|
|
583
|
-
|
|
677
|
+
turnOccurredAt: currentTurnOccurredAt,
|
|
584
678
|
promptText,
|
|
585
679
|
assistantText,
|
|
586
680
|
tokensIn: estimateTokens(promptText),
|
|
@@ -589,6 +683,7 @@ export async function parseCursorTranscriptFile(filePath, composerHeaders, verbo
|
|
|
589
683
|
riskScore: risk.risk_score,
|
|
590
684
|
riskCategories: risk.risk_categories,
|
|
591
685
|
});
|
|
686
|
+
currentTurnOccurredAt = null;
|
|
592
687
|
};
|
|
593
688
|
let lineNumber = 0;
|
|
594
689
|
try {
|
|
@@ -617,9 +712,11 @@ export async function parseCursorTranscriptFile(filePath, composerHeaders, verbo
|
|
|
617
712
|
currentPromptParts = [];
|
|
618
713
|
currentAssistantParts = [];
|
|
619
714
|
}
|
|
715
|
+
noteLineTime(entry);
|
|
620
716
|
currentPromptParts.push(...texts.map(stripUserQueryWrapper).filter((text) => text.length > 0));
|
|
621
717
|
}
|
|
622
718
|
else if (entry.role === "assistant") {
|
|
719
|
+
noteLineTime(entry);
|
|
623
720
|
currentAssistantParts.push(...texts.map((text) => text.trim()).filter((text) => text.length > 0));
|
|
624
721
|
}
|
|
625
722
|
}
|
|
@@ -636,9 +733,16 @@ export async function parseCursorTranscriptFile(filePath, composerHeaders, verbo
|
|
|
636
733
|
}
|
|
637
734
|
finalizeTurn();
|
|
638
735
|
const contentHash = hasher.digest("hex").slice(0, 32);
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
736
|
+
const total = drafts.length;
|
|
737
|
+
return drafts.map((draft, index) => {
|
|
738
|
+
const { turnOccurredAt, ...rest } = draft;
|
|
739
|
+
return {
|
|
740
|
+
...rest,
|
|
741
|
+
contentHash,
|
|
742
|
+
occurredAt: turnOccurredAt ??
|
|
743
|
+
interpolateTurnOccurredAt(sessionStart, sessionEnd, index, total, sessionFallback),
|
|
744
|
+
};
|
|
745
|
+
});
|
|
642
746
|
}
|
|
643
747
|
export async function readCursorTranscriptSessions(cursorUserBaseDir, transcriptProjectDirs, verbose = false) {
|
|
644
748
|
const composerHeaders = readComposerHeaders(cursorUserBaseDir);
|
|
@@ -702,8 +806,12 @@ function pick(obj, ...keys) {
|
|
|
702
806
|
}
|
|
703
807
|
return typeof cur === "number" ? cur : null;
|
|
704
808
|
}
|
|
809
|
+
function dailyStatsSessionId(date, eventType, modelKey) {
|
|
810
|
+
const suffix = modelKey ? `${eventType}:${modelKey}` : eventType;
|
|
811
|
+
return `cursor:daily_stats:${date}:${suffix}`;
|
|
812
|
+
}
|
|
705
813
|
function buildPayload(opts) {
|
|
706
|
-
const { eventType, tokensIn, tokensOut, costUsd, occurredAt, dbPath, model = "unknown", projectId, costModel = LINE_COST_MODEL, } = opts;
|
|
814
|
+
const { eventType, tokensIn, tokensOut, costUsd, occurredAt, dbPath, date, model = "unknown", modelResolution, modelKey, projectId, costModel = LINE_COST_MODEL, } = opts;
|
|
707
815
|
const payload = {
|
|
708
816
|
tool_name: "cursor",
|
|
709
817
|
event_type: eventType,
|
|
@@ -713,18 +821,20 @@ function buildPayload(opts) {
|
|
|
713
821
|
cost_usd: costUsd,
|
|
714
822
|
occurred_at: occurredAt,
|
|
715
823
|
metadata: {
|
|
824
|
+
session_id: dailyStatsSessionId(date, eventType, modelKey),
|
|
716
825
|
cursor_session_id: null,
|
|
717
826
|
...cursorWorkspaceMetadata(dbPath),
|
|
718
827
|
cost_model: costModel,
|
|
719
828
|
scannable: false,
|
|
720
829
|
risk_level: "none",
|
|
830
|
+
...(modelResolution !== undefined ? { model_resolution: modelResolution } : {}),
|
|
721
831
|
},
|
|
722
832
|
};
|
|
723
833
|
if (projectId)
|
|
724
834
|
payload.project_id = projectId;
|
|
725
835
|
return payload;
|
|
726
836
|
}
|
|
727
|
-
export function mapDailyStats(entry, projectId, pricing = DEFAULT_CURSOR_PRICING, model) {
|
|
837
|
+
export function mapDailyStats(entry, projectId, pricing = DEFAULT_CURSOR_PRICING, model, modelResolution) {
|
|
728
838
|
const { date, value, dbPath } = entry;
|
|
729
839
|
const occurredAt = `${date}T00:00:00.000Z`;
|
|
730
840
|
const results = [];
|
|
@@ -740,10 +850,12 @@ export function mapDailyStats(entry, projectId, pricing = DEFAULT_CURSOR_PRICING
|
|
|
740
850
|
eventType: "completion",
|
|
741
851
|
tokensIn: tabSuggested,
|
|
742
852
|
tokensOut: tabAccepted,
|
|
743
|
-
costUsd: computeLineCost("completion",
|
|
853
|
+
costUsd: computeLineCost("completion", tabAccepted, pricing),
|
|
744
854
|
occurredAt,
|
|
745
855
|
dbPath,
|
|
856
|
+
date,
|
|
746
857
|
model,
|
|
858
|
+
modelResolution,
|
|
747
859
|
projectId,
|
|
748
860
|
}));
|
|
749
861
|
}
|
|
@@ -755,7 +867,9 @@ export function mapDailyStats(entry, projectId, pricing = DEFAULT_CURSOR_PRICING
|
|
|
755
867
|
costUsd: computeLineCost("chat", composerSuggested, pricing),
|
|
756
868
|
occurredAt,
|
|
757
869
|
dbPath,
|
|
870
|
+
date,
|
|
758
871
|
model,
|
|
872
|
+
modelResolution,
|
|
759
873
|
projectId,
|
|
760
874
|
}));
|
|
761
875
|
}
|
|
@@ -776,7 +890,9 @@ export function mapDailyStats(entry, projectId, pricing = DEFAULT_CURSOR_PRICING
|
|
|
776
890
|
costUsd: computeTokenCost("chat", tokensIn, tokensOut, pricing),
|
|
777
891
|
occurredAt,
|
|
778
892
|
dbPath,
|
|
893
|
+
date,
|
|
779
894
|
model,
|
|
895
|
+
modelKey: model,
|
|
780
896
|
projectId,
|
|
781
897
|
costModel: TOKEN_COST_MODEL,
|
|
782
898
|
}));
|
|
@@ -788,7 +904,7 @@ export function mapDailyStats(entry, projectId, pricing = DEFAULT_CURSOR_PRICING
|
|
|
788
904
|
* Cursor only keeps one recent commit row (overwritten on each new commit).
|
|
789
905
|
* Line-cost math still follows the chat-style line proxy (`computeLineCost("chat", …)`); only `event_type` differs.
|
|
790
906
|
*/
|
|
791
|
-
export function mapRecentCommit(entry, projectId, pricing = DEFAULT_CURSOR_PRICING, model) {
|
|
907
|
+
export function mapRecentCommit(entry, projectId, pricing = DEFAULT_CURSOR_PRICING, model, modelResolution) {
|
|
792
908
|
const { value: obj, dbPath } = entry;
|
|
793
909
|
const occurredAt = toIsoString(obj.timestamp);
|
|
794
910
|
if (!occurredAt)
|
|
@@ -834,6 +950,7 @@ export function mapRecentCommit(entry, projectId, pricing = DEFAULT_CURSOR_PRICI
|
|
|
834
950
|
: undefined,
|
|
835
951
|
scannable: false,
|
|
836
952
|
risk_level: "none",
|
|
953
|
+
...(modelResolution !== undefined ? { model_resolution: modelResolution } : {}),
|
|
837
954
|
},
|
|
838
955
|
};
|
|
839
956
|
if (projectId)
|
|
@@ -870,7 +987,7 @@ export function mapEvent(row, workspacePath, projectId, pricing = DEFAULT_CURSOR
|
|
|
870
987
|
payload.project_id = projectId;
|
|
871
988
|
return payload;
|
|
872
989
|
}
|
|
873
|
-
export function mapTranscriptTurn(turn, projectId, pricing = DEFAULT_CURSOR_PRICING, model) {
|
|
990
|
+
export function mapTranscriptTurn(turn, projectId, pricing = DEFAULT_CURSOR_PRICING, model, modelResolution) {
|
|
874
991
|
const payload = {
|
|
875
992
|
tool_name: "cursor",
|
|
876
993
|
event_type: "chat",
|
|
@@ -892,6 +1009,7 @@ export function mapTranscriptTurn(turn, projectId, pricing = DEFAULT_CURSOR_PRIC
|
|
|
892
1009
|
composer_name: turn.composerName ?? undefined,
|
|
893
1010
|
prompt_text: turn.promptText || undefined,
|
|
894
1011
|
assistant_text: turn.assistantText || undefined,
|
|
1012
|
+
...(modelResolution !== undefined ? { model_resolution: modelResolution } : {}),
|
|
895
1013
|
},
|
|
896
1014
|
};
|
|
897
1015
|
if (projectId)
|
package/dist/server.d.ts
CHANGED
|
@@ -7,8 +7,25 @@ export declare const SYNC_NOW_INPUT_SCHEMA: z.ZodObject<{
|
|
|
7
7
|
cursor: "cursor";
|
|
8
8
|
}>>>;
|
|
9
9
|
}, z.core.$strict>;
|
|
10
|
-
/**
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Whether a `credential_validation_failed` warning should mirror to stderr.
|
|
12
|
+
* Mirrors for one-shot/startup sources so an installed-but-uninitialized MCP is
|
|
13
|
+
* visible, but NOT for the recurring background `"interval"` tick — that fires
|
|
14
|
+
* every SYNC_INTERVAL_MS for the whole process lifetime and would spam the logs.
|
|
15
|
+
* The event is always written to mcp.log regardless of this return value.
|
|
16
|
+
*/
|
|
17
|
+
export declare function shouldMirrorMissingCredentials(source: string): boolean;
|
|
18
|
+
/** Structured status for `aixle_insights_status` — tolerates missing/malformed credentials and state. */
|
|
19
|
+
export declare function buildAixleInsightsStatusPayload(): Promise<Record<string, unknown>>;
|
|
12
20
|
/** In-process MCP server instance (stdio not attached). */
|
|
13
|
-
export declare function
|
|
21
|
+
export declare function createAixleInsightsMcpServer(): McpServer;
|
|
22
|
+
/**
|
|
23
|
+
* Subscribes `shutdown` to the given stream's "end" and "close" events.
|
|
24
|
+
* Closing stdin is how the OS signals a stdio-transport MCP server that its
|
|
25
|
+
* parent process is gone — this fires even when the parent can't deliver a
|
|
26
|
+
* signal (e.g. it was itself SIGKILL'd, or the OS reparented this process
|
|
27
|
+
* without ever sending one). Exported standalone so it's testable with a
|
|
28
|
+
* plain EventEmitter instead of the real process.stdin.
|
|
29
|
+
*/
|
|
30
|
+
export declare function wireParentExitShutdown(stdin: Pick<NodeJS.ReadStream, "on">, shutdown: () => void): void;
|
|
14
31
|
export declare function startServer(): Promise<void>;
|