@yhong91/vibetime 0.1.57 → 0.1.59
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/bin/vibetime.mjs +741 -87
- package/package.json +1 -1
package/bin/vibetime.mjs
CHANGED
|
@@ -884,7 +884,7 @@ var init_esm = __esm({
|
|
|
884
884
|
|
|
885
885
|
// src/cli.ts
|
|
886
886
|
import { spawn as spawn2, spawnSync } from "node:child_process";
|
|
887
|
-
import { mkdir as
|
|
887
|
+
import { mkdir as mkdir6, open, rm, stat as stat16, writeFile as writeFile5 } from "node:fs/promises";
|
|
888
888
|
import os13 from "node:os";
|
|
889
889
|
import path26 from "node:path";
|
|
890
890
|
import { fileURLToPath } from "node:url";
|
|
@@ -2047,7 +2047,7 @@ function claudeStyleFileMetrics(tool, input) {
|
|
|
2047
2047
|
}
|
|
2048
2048
|
|
|
2049
2049
|
// src/lib/constants.ts
|
|
2050
|
-
var PACKAGE_VERSION = true ? "0.1.
|
|
2050
|
+
var PACKAGE_VERSION = true ? "0.1.59" : "0.1.1";
|
|
2051
2051
|
var GENERATED_MARKER = "Generated by vibetime.";
|
|
2052
2052
|
var DEFAULT_API_URL = "http://121.196.224.82:3001";
|
|
2053
2053
|
var DEFAULT_BACKFILL_BATCH_SIZE = 50;
|
|
@@ -5011,8 +5011,8 @@ async function codebuddyBackfillFiles(sourceRoot, home, env) {
|
|
|
5011
5011
|
}
|
|
5012
5012
|
const filePath = path9.join(traceDir, entry);
|
|
5013
5013
|
try {
|
|
5014
|
-
const
|
|
5015
|
-
files.push({ path: filePath, modifiedAt:
|
|
5014
|
+
const stat17 = await import("node:fs/promises").then((fs) => fs.stat(filePath));
|
|
5015
|
+
files.push({ path: filePath, modifiedAt: stat17.mtime.toISOString(), groupId: pidDir.name });
|
|
5016
5016
|
} catch {
|
|
5017
5017
|
}
|
|
5018
5018
|
}
|
|
@@ -5099,7 +5099,7 @@ function fileActivitiesFromPatchChanges(changes, ts, cwd, displayFilePath3) {
|
|
|
5099
5099
|
}
|
|
5100
5100
|
|
|
5101
5101
|
// src/lib/session-context.ts
|
|
5102
|
-
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
5102
|
+
import { mkdir as mkdir3, stat as stat6, writeFile as writeFile3 } from "node:fs/promises";
|
|
5103
5103
|
import os4 from "node:os";
|
|
5104
5104
|
import path10 from "node:path";
|
|
5105
5105
|
init_fs();
|
|
@@ -5126,32 +5126,86 @@ async function readPersistedSessionContext(home, sessionId) {
|
|
|
5126
5126
|
}
|
|
5127
5127
|
const cwd = typeof raw.cwd === "string" && raw.cwd.length > 0 ? raw.cwd : void 0;
|
|
5128
5128
|
const project = typeof raw.project === "string" && raw.project.length > 0 ? raw.project : void 0;
|
|
5129
|
-
|
|
5129
|
+
const usage = Array.isArray(raw.usage) ? raw.usage.filter((item) => {
|
|
5130
|
+
return isPlainObject(item) && typeof item.generationId === "string" && item.generationId.length > 0 && typeof item.ts === "string";
|
|
5131
|
+
}) : void 0;
|
|
5132
|
+
return { version: SESSION_CONTEXT_VERSION, sessionId, cwd, project, updatedAt: raw.updatedAt, usage };
|
|
5130
5133
|
}
|
|
5131
5134
|
async function readPersistedSessionContextFromOptions(options, sessionId) {
|
|
5132
5135
|
return readPersistedSessionContext(resolveHome2(options), sessionId);
|
|
5133
5136
|
}
|
|
5137
|
+
async function persistedSessionContextModifiedAt(home, sessionId) {
|
|
5138
|
+
const info = await stat6(sessionContextPath(home, sessionId)).catch(() => null);
|
|
5139
|
+
return info?.isFile() ? info.mtime : void 0;
|
|
5140
|
+
}
|
|
5134
5141
|
async function persistHookSessionContext(home, payload) {
|
|
5135
5142
|
if (!isPlainObject(payload)) {
|
|
5136
5143
|
return;
|
|
5137
5144
|
}
|
|
5138
|
-
const sessionId =
|
|
5139
|
-
const
|
|
5140
|
-
|
|
5145
|
+
const sessionId = hookSessionId(payload);
|
|
5146
|
+
const cwdRaw = typeof payload.cwd === "string" ? payload.cwd.trim() : "";
|
|
5147
|
+
const cwd = cwdRaw && path10.isAbsolute(cwdRaw) ? cwdRaw : void 0;
|
|
5148
|
+
const usage = hookUsageFromPayload(payload);
|
|
5149
|
+
if (!sessionId || !cwd && !usage) {
|
|
5141
5150
|
return;
|
|
5142
5151
|
}
|
|
5143
|
-
const
|
|
5152
|
+
const existing = await readPersistedSessionContext(home, sessionId);
|
|
5153
|
+
const nextUsage = mergeHookUsage(existing?.usage, usage);
|
|
5154
|
+
const nextCwd = cwd || existing?.cwd;
|
|
5144
5155
|
const context = {
|
|
5145
5156
|
version: SESSION_CONTEXT_VERSION,
|
|
5146
5157
|
sessionId,
|
|
5147
|
-
cwd,
|
|
5148
|
-
project,
|
|
5149
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5158
|
+
cwd: nextCwd,
|
|
5159
|
+
project: nextCwd ? path10.basename(nextCwd) : existing?.project,
|
|
5160
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5161
|
+
usage: nextUsage
|
|
5150
5162
|
};
|
|
5151
5163
|
await mkdir3(sessionContextDir(home), { recursive: true });
|
|
5152
5164
|
await writeFile3(sessionContextPath(home, sessionId), `${JSON.stringify(context, null, 2)}
|
|
5153
5165
|
`, "utf8");
|
|
5154
5166
|
}
|
|
5167
|
+
function hookSessionId(payload) {
|
|
5168
|
+
for (const key of ["session_id", "sessionId", "conversation_id", "conversationId"]) {
|
|
5169
|
+
const value = stringField(payload, key)?.trim();
|
|
5170
|
+
if (value) {
|
|
5171
|
+
return value;
|
|
5172
|
+
}
|
|
5173
|
+
}
|
|
5174
|
+
return "";
|
|
5175
|
+
}
|
|
5176
|
+
function hookUsageFromPayload(payload) {
|
|
5177
|
+
const nested = objectField(payload, "token_usage").inputTokens !== void 0 || objectField(payload, "tokenUsage").inputTokens !== void 0 ? { ...objectField(payload, "token_usage"), ...objectField(payload, "tokenUsage") } : payload;
|
|
5178
|
+
const tokensInput = numberField(nested, "input_tokens") ?? numberField(nested, "inputTokens") ?? numberField(payload, "input_tokens") ?? numberField(payload, "inputTokens");
|
|
5179
|
+
const tokensOutput = numberField(nested, "output_tokens") ?? numberField(nested, "outputTokens") ?? numberField(payload, "output_tokens") ?? numberField(payload, "outputTokens");
|
|
5180
|
+
const tokensCacheReadInput = numberField(nested, "cache_read_tokens") ?? numberField(nested, "cacheReadTokens") ?? numberField(payload, "cache_read_tokens") ?? numberField(payload, "cacheReadTokens");
|
|
5181
|
+
const tokensCacheCreationInput = numberField(nested, "cache_write_tokens") ?? numberField(nested, "cacheWriteTokens") ?? numberField(payload, "cache_write_tokens") ?? numberField(payload, "cacheWriteTokens");
|
|
5182
|
+
if (!tokensInput && !tokensOutput && !tokensCacheReadInput && !tokensCacheCreationInput) {
|
|
5183
|
+
return void 0;
|
|
5184
|
+
}
|
|
5185
|
+
const generationId = stringField(payload, "generation_id")?.trim() || stringField(payload, "generationId")?.trim() || stringField(payload, "request_id")?.trim() || stringField(payload, "requestId")?.trim() || `${tokensInput || 0}:${tokensOutput || 0}:${tokensCacheReadInput || 0}:${tokensCacheCreationInput || 0}`;
|
|
5186
|
+
return {
|
|
5187
|
+
generationId,
|
|
5188
|
+
ts: stringField(payload, "timestamp") || (/* @__PURE__ */ new Date()).toISOString(),
|
|
5189
|
+
model: stringField(payload, "model") || stringField(payload, "model_name"),
|
|
5190
|
+
tokensInput: tokensInput || void 0,
|
|
5191
|
+
tokensOutput: tokensOutput || void 0,
|
|
5192
|
+
tokensCacheReadInput: tokensCacheReadInput || void 0,
|
|
5193
|
+
tokensCacheCreationInput: tokensCacheCreationInput || void 0
|
|
5194
|
+
};
|
|
5195
|
+
}
|
|
5196
|
+
function mergeHookUsage(existing, next) {
|
|
5197
|
+
if (!next) {
|
|
5198
|
+
return existing;
|
|
5199
|
+
}
|
|
5200
|
+
const current = existing ? [...existing] : [];
|
|
5201
|
+
const index = current.findIndex((item) => item.generationId === next.generationId);
|
|
5202
|
+
if (index >= 0) {
|
|
5203
|
+
current[index] = next;
|
|
5204
|
+
} else {
|
|
5205
|
+
current.push(next);
|
|
5206
|
+
}
|
|
5207
|
+
return current;
|
|
5208
|
+
}
|
|
5155
5209
|
|
|
5156
5210
|
// src/adapters/codex.ts
|
|
5157
5211
|
async function parseCodexSessionFile(filePath, options) {
|
|
@@ -5841,7 +5895,7 @@ function createCodexAdapter() {
|
|
|
5841
5895
|
}
|
|
5842
5896
|
|
|
5843
5897
|
// src/adapters/copilot.ts
|
|
5844
|
-
import { readdir as readdir5, readFile as readFile7, stat as
|
|
5898
|
+
import { readdir as readdir5, readFile as readFile7, stat as stat7 } from "node:fs/promises";
|
|
5845
5899
|
import os5 from "node:os";
|
|
5846
5900
|
import path12 from "node:path";
|
|
5847
5901
|
async function parseCopilotSessionFile(filePath, options) {
|
|
@@ -6182,7 +6236,7 @@ async function copilotBackfillFiles(sourceRoot, home = os5.homedir(), _env) {
|
|
|
6182
6236
|
continue;
|
|
6183
6237
|
}
|
|
6184
6238
|
const eventsPath = path12.join(sessionDir, entry, "events.jsonl");
|
|
6185
|
-
const info = await
|
|
6239
|
+
const info = await stat7(eventsPath).catch(() => null);
|
|
6186
6240
|
if (info) {
|
|
6187
6241
|
results.push({ path: eventsPath, modifiedAt: info.mtime.toISOString() });
|
|
6188
6242
|
}
|
|
@@ -6238,7 +6292,7 @@ function createCopilotAdapter() {
|
|
|
6238
6292
|
}
|
|
6239
6293
|
|
|
6240
6294
|
// src/adapters/cursor.ts
|
|
6241
|
-
import { copyFile, readdir as readdir6, readFile as readFile8, stat as
|
|
6295
|
+
import { copyFile, mkdir as mkdir4, readdir as readdir6, readFile as readFile8, stat as stat8, writeFile as writeFile4 } from "node:fs/promises";
|
|
6242
6296
|
import os6 from "node:os";
|
|
6243
6297
|
import path13 from "node:path";
|
|
6244
6298
|
|
|
@@ -6346,6 +6400,222 @@ function collectCursorHookCommands(content) {
|
|
|
6346
6400
|
|
|
6347
6401
|
// src/adapters/cursor.ts
|
|
6348
6402
|
init_fs();
|
|
6403
|
+
|
|
6404
|
+
// src/adapters/cursor-cloud-usage.ts
|
|
6405
|
+
var CURSOR_CLOUD_USAGE_GENERATION_ID = "cursor-cloud";
|
|
6406
|
+
var CURSOR_CLOUD_USAGE_FILENAME = "cursor-cloud-usage.json";
|
|
6407
|
+
var CURSOR_CLOUD_AGENT_PROJECT = "Cloud Agent";
|
|
6408
|
+
var CURSOR_DASHBOARD_USAGE_URL = "https://cursor.com/api/dashboard/get-filtered-usage-events";
|
|
6409
|
+
var CURSOR_CLOUD_USAGE_CACHE_VERSION = 1;
|
|
6410
|
+
var DEFAULT_WINDOW_DAYS = 90;
|
|
6411
|
+
var PAGE_SIZE = 1e3;
|
|
6412
|
+
function cursorCloudUsageDisabled(env = process.env) {
|
|
6413
|
+
const value = env.VIBETIME_CURSOR_CLOUD_USAGE;
|
|
6414
|
+
return value === "0" || value === "false";
|
|
6415
|
+
}
|
|
6416
|
+
function cursorSessionCookie(accessToken) {
|
|
6417
|
+
const token = accessToken.trim();
|
|
6418
|
+
if (!token) {
|
|
6419
|
+
return void 0;
|
|
6420
|
+
}
|
|
6421
|
+
const payload = jwtPayload(token);
|
|
6422
|
+
const userId = String(payload?.sub || "").split("|").at(-1)?.trim();
|
|
6423
|
+
if (!userId) {
|
|
6424
|
+
return void 0;
|
|
6425
|
+
}
|
|
6426
|
+
return `WorkosCursorSessionToken=${userId}%3A%3A${token}`;
|
|
6427
|
+
}
|
|
6428
|
+
function cursorCloudEventFromDashboard(event) {
|
|
6429
|
+
if (!isPlainObject(event)) {
|
|
6430
|
+
return void 0;
|
|
6431
|
+
}
|
|
6432
|
+
const conversationId = stringField(event, "conversationId")?.trim();
|
|
6433
|
+
if (!conversationId) {
|
|
6434
|
+
return void 0;
|
|
6435
|
+
}
|
|
6436
|
+
const tokenUsage = objectField(event, "tokenUsage");
|
|
6437
|
+
const tokensInput = numberField(tokenUsage, "inputTokens") || 0;
|
|
6438
|
+
const tokensOutput = numberField(tokenUsage, "outputTokens") || 0;
|
|
6439
|
+
const tokensCacheReadInput = numberField(tokenUsage, "cacheReadTokens") || 0;
|
|
6440
|
+
const tokensCacheCreationInput = numberField(tokenUsage, "cacheWriteTokens") || 0;
|
|
6441
|
+
if (tokensInput <= 0 && tokensOutput <= 0 && tokensCacheReadInput <= 0 && tokensCacheCreationInput <= 0) {
|
|
6442
|
+
return void 0;
|
|
6443
|
+
}
|
|
6444
|
+
return {
|
|
6445
|
+
conversationId,
|
|
6446
|
+
model: stringField(event, "model") || void 0,
|
|
6447
|
+
ts: timestampFrom(event.timestamp) || (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
6448
|
+
tokensInput,
|
|
6449
|
+
tokensOutput,
|
|
6450
|
+
tokensCacheReadInput,
|
|
6451
|
+
tokensCacheCreationInput
|
|
6452
|
+
};
|
|
6453
|
+
}
|
|
6454
|
+
function groupCursorUsageEvents(events) {
|
|
6455
|
+
const byConversation = /* @__PURE__ */ new Map();
|
|
6456
|
+
for (const event of events) {
|
|
6457
|
+
const parsed = cursorCloudEventFromDashboard(event);
|
|
6458
|
+
if (!parsed) {
|
|
6459
|
+
continue;
|
|
6460
|
+
}
|
|
6461
|
+
const current = byConversation.get(parsed.conversationId);
|
|
6462
|
+
if (current) {
|
|
6463
|
+
current.push(parsed);
|
|
6464
|
+
} else {
|
|
6465
|
+
byConversation.set(parsed.conversationId, [parsed]);
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
return byConversation;
|
|
6469
|
+
}
|
|
6470
|
+
function sumCursorCloudEvents(conversationId, events) {
|
|
6471
|
+
if (events.length === 0) {
|
|
6472
|
+
return void 0;
|
|
6473
|
+
}
|
|
6474
|
+
const first = events[0];
|
|
6475
|
+
const firstStart = first.startedAt || first.ts;
|
|
6476
|
+
const summed = {
|
|
6477
|
+
conversationId,
|
|
6478
|
+
model: first.model,
|
|
6479
|
+
ts: first.ts,
|
|
6480
|
+
startedAt: firstStart,
|
|
6481
|
+
tokensInput: 0,
|
|
6482
|
+
tokensOutput: 0,
|
|
6483
|
+
tokensCacheReadInput: 0,
|
|
6484
|
+
tokensCacheCreationInput: 0
|
|
6485
|
+
};
|
|
6486
|
+
for (const event of events) {
|
|
6487
|
+
summed.tokensInput += event.tokensInput;
|
|
6488
|
+
summed.tokensOutput += event.tokensOutput;
|
|
6489
|
+
summed.tokensCacheReadInput += event.tokensCacheReadInput;
|
|
6490
|
+
summed.tokensCacheCreationInput += event.tokensCacheCreationInput;
|
|
6491
|
+
const eventStart = event.startedAt || event.ts;
|
|
6492
|
+
if (Date.parse(eventStart) < Date.parse(summed.startedAt)) {
|
|
6493
|
+
summed.startedAt = eventStart;
|
|
6494
|
+
}
|
|
6495
|
+
if (Date.parse(event.ts) >= Date.parse(summed.ts)) {
|
|
6496
|
+
summed.ts = event.ts;
|
|
6497
|
+
if (event.model) {
|
|
6498
|
+
summed.model = event.model;
|
|
6499
|
+
}
|
|
6500
|
+
}
|
|
6501
|
+
}
|
|
6502
|
+
return summed;
|
|
6503
|
+
}
|
|
6504
|
+
function hookUncachedInputTokens(turn) {
|
|
6505
|
+
return Math.max(0, (turn.tokensInput || 0) - (turn.tokensCacheReadInput || 0));
|
|
6506
|
+
}
|
|
6507
|
+
function cloudEventMatchesHookTurn(event, turn) {
|
|
6508
|
+
return event.tokensInput === hookUncachedInputTokens(turn) && event.tokensOutput === (turn.tokensOutput || 0) && event.tokensCacheReadInput === (turn.tokensCacheReadInput || 0);
|
|
6509
|
+
}
|
|
6510
|
+
function unmatchedCloudEvents(events, turns) {
|
|
6511
|
+
const used = /* @__PURE__ */ new Set();
|
|
6512
|
+
for (const turn of turns) {
|
|
6513
|
+
const index = events.findIndex((event, i) => !used.has(i) && cloudEventMatchesHookTurn(event, turn));
|
|
6514
|
+
if (index >= 0) {
|
|
6515
|
+
used.add(index);
|
|
6516
|
+
}
|
|
6517
|
+
}
|
|
6518
|
+
return events.filter((_, i) => !used.has(i));
|
|
6519
|
+
}
|
|
6520
|
+
async function fetchCursorDashboardUsage(args) {
|
|
6521
|
+
const cookie = cursorSessionCookie(args.accessToken);
|
|
6522
|
+
if (!cookie) {
|
|
6523
|
+
return /* @__PURE__ */ new Map();
|
|
6524
|
+
}
|
|
6525
|
+
const fetchImpl = args.fetchImpl || fetch;
|
|
6526
|
+
const now = args.now || /* @__PURE__ */ new Date();
|
|
6527
|
+
const windowDays = args.windowDays ?? DEFAULT_WINDOW_DAYS;
|
|
6528
|
+
const pageSize = args.pageSize ?? PAGE_SIZE;
|
|
6529
|
+
const start = new Date(now.getTime() - windowDays * 24 * 60 * 60 * 1e3);
|
|
6530
|
+
const events = [];
|
|
6531
|
+
let page = 1;
|
|
6532
|
+
let total = Number.POSITIVE_INFINITY;
|
|
6533
|
+
while ((page - 1) * pageSize < total) {
|
|
6534
|
+
const body = JSON.stringify({
|
|
6535
|
+
page,
|
|
6536
|
+
pageSize,
|
|
6537
|
+
startDate: String(start.getTime()),
|
|
6538
|
+
endDate: String(now.getTime())
|
|
6539
|
+
});
|
|
6540
|
+
const response = await fetchImpl(CURSOR_DASHBOARD_USAGE_URL, {
|
|
6541
|
+
method: "POST",
|
|
6542
|
+
headers: {
|
|
6543
|
+
"Content-Type": "application/json",
|
|
6544
|
+
Accept: "application/json",
|
|
6545
|
+
Cookie: cookie,
|
|
6546
|
+
Origin: "https://cursor.com",
|
|
6547
|
+
Referer: "https://cursor.com/settings",
|
|
6548
|
+
"User-Agent": "vibetime-cli"
|
|
6549
|
+
},
|
|
6550
|
+
body,
|
|
6551
|
+
signal: AbortSignal.timeout(2e4)
|
|
6552
|
+
});
|
|
6553
|
+
if (!response.ok) {
|
|
6554
|
+
return /* @__PURE__ */ new Map();
|
|
6555
|
+
}
|
|
6556
|
+
const payload = await response.json();
|
|
6557
|
+
if (!isPlainObject(payload)) {
|
|
6558
|
+
return /* @__PURE__ */ new Map();
|
|
6559
|
+
}
|
|
6560
|
+
const pageEvents = Array.isArray(payload.usageEventsDisplay) ? payload.usageEventsDisplay : [];
|
|
6561
|
+
events.push(...pageEvents);
|
|
6562
|
+
const reported = numberField(payload, "totalUsageEventsCount");
|
|
6563
|
+
total = reported ?? pageEvents.length;
|
|
6564
|
+
if (pageEvents.length < pageSize) {
|
|
6565
|
+
break;
|
|
6566
|
+
}
|
|
6567
|
+
page += 1;
|
|
6568
|
+
}
|
|
6569
|
+
return groupCursorUsageEvents(events);
|
|
6570
|
+
}
|
|
6571
|
+
function serializeCursorCloudUsageCache(sessions) {
|
|
6572
|
+
const sorted = [...sessions].sort((a, b) => a.conversationId.localeCompare(b.conversationId));
|
|
6573
|
+
return `${JSON.stringify({ version: CURSOR_CLOUD_USAGE_CACHE_VERSION, sessions: sorted }, null, 2)}
|
|
6574
|
+
`;
|
|
6575
|
+
}
|
|
6576
|
+
function parseCursorCloudUsageCache(raw) {
|
|
6577
|
+
if (!isPlainObject(raw) || raw.version !== CURSOR_CLOUD_USAGE_CACHE_VERSION || !Array.isArray(raw.sessions)) {
|
|
6578
|
+
return [];
|
|
6579
|
+
}
|
|
6580
|
+
const sessions = [];
|
|
6581
|
+
for (const item of raw.sessions) {
|
|
6582
|
+
if (!isPlainObject(item)) {
|
|
6583
|
+
continue;
|
|
6584
|
+
}
|
|
6585
|
+
const conversationId = stringField(item, "conversationId")?.trim();
|
|
6586
|
+
const ts = stringField(item, "ts");
|
|
6587
|
+
const startedAt = stringField(item, "startedAt") || ts;
|
|
6588
|
+
if (!conversationId || !ts || !startedAt) {
|
|
6589
|
+
continue;
|
|
6590
|
+
}
|
|
6591
|
+
sessions.push({
|
|
6592
|
+
conversationId,
|
|
6593
|
+
model: stringField(item, "model"),
|
|
6594
|
+
ts,
|
|
6595
|
+
startedAt,
|
|
6596
|
+
tokensInput: numberField(item, "tokensInput") || 0,
|
|
6597
|
+
tokensOutput: numberField(item, "tokensOutput") || 0,
|
|
6598
|
+
tokensCacheReadInput: numberField(item, "tokensCacheReadInput") || 0,
|
|
6599
|
+
tokensCacheCreationInput: numberField(item, "tokensCacheCreationInput") || 0
|
|
6600
|
+
});
|
|
6601
|
+
}
|
|
6602
|
+
return sessions;
|
|
6603
|
+
}
|
|
6604
|
+
function jwtPayload(token) {
|
|
6605
|
+
const segment = token.split(".")[1];
|
|
6606
|
+
if (!segment) {
|
|
6607
|
+
return void 0;
|
|
6608
|
+
}
|
|
6609
|
+
try {
|
|
6610
|
+
const json = Buffer.from(segment, "base64url").toString("utf8");
|
|
6611
|
+
const parsed = JSON.parse(json);
|
|
6612
|
+
return isPlainObject(parsed) ? parsed : void 0;
|
|
6613
|
+
} catch {
|
|
6614
|
+
return void 0;
|
|
6615
|
+
}
|
|
6616
|
+
}
|
|
6617
|
+
|
|
6618
|
+
// src/adapters/cursor.ts
|
|
6349
6619
|
var SOURCE_ID = "cursor";
|
|
6350
6620
|
var AGENT_NAME = "cursor";
|
|
6351
6621
|
var HOOK_COMMAND = `vibetime hook --agent ${SOURCE_ID}`;
|
|
@@ -6362,6 +6632,7 @@ var CURSOR_HOOK_EVENTS = [
|
|
|
6362
6632
|
"subagentStart",
|
|
6363
6633
|
"subagentStop",
|
|
6364
6634
|
"stop",
|
|
6635
|
+
"afterAgentResponse",
|
|
6365
6636
|
"afterFileEdit",
|
|
6366
6637
|
"afterShellExecution",
|
|
6367
6638
|
"preCompact"
|
|
@@ -6413,6 +6684,297 @@ function cursorStateDbCandidates(home, env) {
|
|
|
6413
6684
|
}
|
|
6414
6685
|
return candidates;
|
|
6415
6686
|
}
|
|
6687
|
+
var cursorCloudUsageByOptions = /* @__PURE__ */ new WeakMap();
|
|
6688
|
+
function isCursorStateDbPath(filePath) {
|
|
6689
|
+
if (!filePath) {
|
|
6690
|
+
return false;
|
|
6691
|
+
}
|
|
6692
|
+
const base = path13.basename(filePath);
|
|
6693
|
+
return base === "state.vscdb" || base.endsWith(".vscdb");
|
|
6694
|
+
}
|
|
6695
|
+
function injectedCloudEvent(conversationId, item) {
|
|
6696
|
+
const tokensInput = numberField(item, "tokensInput") || numberField(item, "inputTokens") || 0;
|
|
6697
|
+
const tokensOutput = numberField(item, "tokensOutput") || numberField(item, "outputTokens") || 0;
|
|
6698
|
+
const tokensCacheReadInput = numberField(item, "tokensCacheReadInput") || numberField(item, "cacheReadTokens") || 0;
|
|
6699
|
+
const tokensCacheCreationInput = numberField(item, "tokensCacheCreationInput") || numberField(item, "cacheWriteTokens") || 0;
|
|
6700
|
+
if (tokensInput <= 0 && tokensOutput <= 0 && tokensCacheReadInput <= 0 && tokensCacheCreationInput <= 0) {
|
|
6701
|
+
return void 0;
|
|
6702
|
+
}
|
|
6703
|
+
const ts = stringField(item, "ts") || (/* @__PURE__ */ new Date(0)).toISOString();
|
|
6704
|
+
return {
|
|
6705
|
+
conversationId,
|
|
6706
|
+
model: stringField(item, "model"),
|
|
6707
|
+
ts,
|
|
6708
|
+
startedAt: stringField(item, "startedAt") || void 0,
|
|
6709
|
+
tokensInput,
|
|
6710
|
+
tokensOutput,
|
|
6711
|
+
tokensCacheReadInput,
|
|
6712
|
+
tokensCacheCreationInput
|
|
6713
|
+
};
|
|
6714
|
+
}
|
|
6715
|
+
function injectedCursorCloudUsage(options) {
|
|
6716
|
+
if (!Object.prototype.hasOwnProperty.call(options, "cursorCloudUsage")) {
|
|
6717
|
+
return void 0;
|
|
6718
|
+
}
|
|
6719
|
+
const raw = options.cursorCloudUsage;
|
|
6720
|
+
const map = /* @__PURE__ */ new Map();
|
|
6721
|
+
if (!isPlainObject(raw)) {
|
|
6722
|
+
return map;
|
|
6723
|
+
}
|
|
6724
|
+
for (const [conversationId, item] of Object.entries(raw)) {
|
|
6725
|
+
if (!conversationId) {
|
|
6726
|
+
continue;
|
|
6727
|
+
}
|
|
6728
|
+
const events = [];
|
|
6729
|
+
if (Array.isArray(item)) {
|
|
6730
|
+
for (const entry of item) {
|
|
6731
|
+
if (isPlainObject(entry)) {
|
|
6732
|
+
const parsed = injectedCloudEvent(conversationId, entry);
|
|
6733
|
+
if (parsed) {
|
|
6734
|
+
events.push(parsed);
|
|
6735
|
+
}
|
|
6736
|
+
}
|
|
6737
|
+
}
|
|
6738
|
+
} else if (isPlainObject(item) && Array.isArray(item.events)) {
|
|
6739
|
+
for (const entry of item.events) {
|
|
6740
|
+
if (isPlainObject(entry)) {
|
|
6741
|
+
const parsed = injectedCloudEvent(conversationId, entry);
|
|
6742
|
+
if (parsed) {
|
|
6743
|
+
events.push(parsed);
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
}
|
|
6747
|
+
} else if (isPlainObject(item)) {
|
|
6748
|
+
const parsed = injectedCloudEvent(conversationId, item);
|
|
6749
|
+
if (parsed) {
|
|
6750
|
+
events.push(parsed);
|
|
6751
|
+
}
|
|
6752
|
+
}
|
|
6753
|
+
if (events.length > 0) {
|
|
6754
|
+
map.set(conversationId, events);
|
|
6755
|
+
}
|
|
6756
|
+
}
|
|
6757
|
+
return map;
|
|
6758
|
+
}
|
|
6759
|
+
async function readCursorAccessToken(dbPath) {
|
|
6760
|
+
if (!isCursorStateDbPath(dbPath)) {
|
|
6761
|
+
return void 0;
|
|
6762
|
+
}
|
|
6763
|
+
const info = await stat8(dbPath).catch(() => null);
|
|
6764
|
+
if (!info) {
|
|
6765
|
+
return void 0;
|
|
6766
|
+
}
|
|
6767
|
+
try {
|
|
6768
|
+
const opened = await openCursorDb(dbPath);
|
|
6769
|
+
try {
|
|
6770
|
+
const row = opened.db.prepare(
|
|
6771
|
+
"SELECT value FROM ItemTable WHERE key = 'cursorAuth/accessToken'"
|
|
6772
|
+
).get();
|
|
6773
|
+
const token = decodeKv(row?.value)?.trim();
|
|
6774
|
+
return token || void 0;
|
|
6775
|
+
} finally {
|
|
6776
|
+
opened.db.close();
|
|
6777
|
+
await opened.cleanup();
|
|
6778
|
+
}
|
|
6779
|
+
} catch {
|
|
6780
|
+
return void 0;
|
|
6781
|
+
}
|
|
6782
|
+
}
|
|
6783
|
+
async function loadCursorCloudUsageMap(options, filePath) {
|
|
6784
|
+
const injected = injectedCursorCloudUsage(options);
|
|
6785
|
+
if (injected) {
|
|
6786
|
+
return injected;
|
|
6787
|
+
}
|
|
6788
|
+
if (cursorCloudUsageDisabled()) {
|
|
6789
|
+
return /* @__PURE__ */ new Map();
|
|
6790
|
+
}
|
|
6791
|
+
const cached = cursorCloudUsageByOptions.get(options);
|
|
6792
|
+
if (cached) {
|
|
6793
|
+
return cached;
|
|
6794
|
+
}
|
|
6795
|
+
const pending = (async () => {
|
|
6796
|
+
try {
|
|
6797
|
+
const token = isCursorStateDbPath(filePath) ? await readCursorAccessToken(filePath) : await readCursorAccessTokenFromHome(options);
|
|
6798
|
+
if (!token) {
|
|
6799
|
+
return /* @__PURE__ */ new Map();
|
|
6800
|
+
}
|
|
6801
|
+
const fetchImpl = typeof options.cursorCloudFetch === "function" ? options.cursorCloudFetch : void 0;
|
|
6802
|
+
return await fetchCursorDashboardUsage({ accessToken: token, fetchImpl });
|
|
6803
|
+
} catch {
|
|
6804
|
+
return /* @__PURE__ */ new Map();
|
|
6805
|
+
}
|
|
6806
|
+
})();
|
|
6807
|
+
cursorCloudUsageByOptions.set(options, pending);
|
|
6808
|
+
return pending;
|
|
6809
|
+
}
|
|
6810
|
+
async function readCursorAccessTokenFromHome(options) {
|
|
6811
|
+
const home = stringOption(options.home) || os6.homedir();
|
|
6812
|
+
const env = {
|
|
6813
|
+
CURSOR_HOME: process.env.CURSOR_HOME,
|
|
6814
|
+
CURSOR_USER_DIR: process.env.CURSOR_USER_DIR
|
|
6815
|
+
};
|
|
6816
|
+
for (const candidate of cursorStateDbCandidates(home, env)) {
|
|
6817
|
+
const token = await readCursorAccessToken(candidate);
|
|
6818
|
+
if (token) {
|
|
6819
|
+
return token;
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6822
|
+
return void 0;
|
|
6823
|
+
}
|
|
6824
|
+
function cloudUsageToPersisted(row, index = 0) {
|
|
6825
|
+
return {
|
|
6826
|
+
generationId: `${CURSOR_CLOUD_USAGE_GENERATION_ID}:${index}:${row.tokensInput}:${row.tokensOutput}:${row.tokensCacheReadInput}`,
|
|
6827
|
+
ts: row.ts,
|
|
6828
|
+
model: row.model,
|
|
6829
|
+
tokensInput: row.tokensInput || void 0,
|
|
6830
|
+
tokensOutput: row.tokensOutput || void 0,
|
|
6831
|
+
tokensCacheReadInput: row.tokensCacheReadInput || void 0,
|
|
6832
|
+
tokensCacheCreationInput: row.tokensCacheCreationInput || void 0
|
|
6833
|
+
};
|
|
6834
|
+
}
|
|
6835
|
+
async function resolveCursorSessionUsage(options, sessionId, filePath) {
|
|
6836
|
+
const persisted = await readPersistedSessionContextFromOptions(options, sessionId);
|
|
6837
|
+
const cloudEvents = (await loadCursorCloudUsageMap(options, filePath)).get(sessionId) || [];
|
|
6838
|
+
if (persisted?.usage?.length) {
|
|
6839
|
+
return [
|
|
6840
|
+
...persisted.usage,
|
|
6841
|
+
...unmatchedCloudEvents(cloudEvents, persisted.usage).map((event, index) => cloudUsageToPersisted(event, index))
|
|
6842
|
+
];
|
|
6843
|
+
}
|
|
6844
|
+
return cloudEvents.map((event, index) => cloudUsageToPersisted(event, index));
|
|
6845
|
+
}
|
|
6846
|
+
function cursorCloudUsageCachePath(home) {
|
|
6847
|
+
return path13.join(home, ".vibetime", CURSOR_CLOUD_USAGE_FILENAME);
|
|
6848
|
+
}
|
|
6849
|
+
async function listLocalCursorSessionIds(home, env) {
|
|
6850
|
+
const ids = /* @__PURE__ */ new Set();
|
|
6851
|
+
for (const dbPath of cursorStateDbCandidates(home, env)) {
|
|
6852
|
+
const composerIds = await listCursorComposerIds(dbPath);
|
|
6853
|
+
for (const id of composerIds) {
|
|
6854
|
+
ids.add(id);
|
|
6855
|
+
}
|
|
6856
|
+
if (composerIds.size > 0 || await stat8(dbPath).catch(() => null)) {
|
|
6857
|
+
break;
|
|
6858
|
+
}
|
|
6859
|
+
}
|
|
6860
|
+
const projects = cursorProjectsDir(home, env);
|
|
6861
|
+
for (const filePath of await listJsonlFiles(projects)) {
|
|
6862
|
+
if (!isCursorTranscriptPath(filePath) && path13.basename(path13.dirname(filePath)) !== "agent-transcripts") {
|
|
6863
|
+
continue;
|
|
6864
|
+
}
|
|
6865
|
+
const sessionId = path13.basename(filePath, ".jsonl");
|
|
6866
|
+
if (sessionId) {
|
|
6867
|
+
ids.add(sessionId);
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6870
|
+
return ids;
|
|
6871
|
+
}
|
|
6872
|
+
async function writeCursorCloudUsageCache(cachePath, sessions) {
|
|
6873
|
+
const next = serializeCursorCloudUsageCache(sessions);
|
|
6874
|
+
const existing = await readFile8(cachePath, "utf8").catch(() => "");
|
|
6875
|
+
if (existing === next) {
|
|
6876
|
+
const info2 = await stat8(cachePath);
|
|
6877
|
+
return info2.mtime.toISOString();
|
|
6878
|
+
}
|
|
6879
|
+
await mkdir4(path13.dirname(cachePath), { recursive: true });
|
|
6880
|
+
await writeFile4(cachePath, next, "utf8");
|
|
6881
|
+
const info = await stat8(cachePath);
|
|
6882
|
+
return info.mtime.toISOString();
|
|
6883
|
+
}
|
|
6884
|
+
async function appendCursorCloudAgentSource(files, home, env, options) {
|
|
6885
|
+
const dbPath = files.find((file) => isCursorStateDbPath(file.path))?.path;
|
|
6886
|
+
const map = await loadCursorCloudUsageMap(options, dbPath);
|
|
6887
|
+
const localIds = await listLocalCursorSessionIds(home, env);
|
|
6888
|
+
const cloudOnly = [];
|
|
6889
|
+
for (const [conversationId, events] of map) {
|
|
6890
|
+
if (localIds.has(conversationId)) {
|
|
6891
|
+
continue;
|
|
6892
|
+
}
|
|
6893
|
+
const summed = sumCursorCloudEvents(conversationId, events);
|
|
6894
|
+
if (summed) {
|
|
6895
|
+
cloudOnly.push(summed);
|
|
6896
|
+
}
|
|
6897
|
+
}
|
|
6898
|
+
if (cloudOnly.length === 0) {
|
|
6899
|
+
return;
|
|
6900
|
+
}
|
|
6901
|
+
const cachePath = cursorCloudUsageCachePath(home);
|
|
6902
|
+
files.push({
|
|
6903
|
+
path: cachePath,
|
|
6904
|
+
modifiedAt: await writeCursorCloudUsageCache(cachePath, cloudOnly)
|
|
6905
|
+
});
|
|
6906
|
+
}
|
|
6907
|
+
function parseCursorCloudAgentSessions(filePath, options, sessions, localIds) {
|
|
6908
|
+
const events = [];
|
|
6909
|
+
const sourcePathHash = `sha256:${createStableHash(filePath)}`;
|
|
6910
|
+
const project = CURSOR_CLOUD_AGENT_PROJECT;
|
|
6911
|
+
const workspaceId = createWorkspaceId({ projectName: project });
|
|
6912
|
+
let lineNumber = 0;
|
|
6913
|
+
for (const row of sessions) {
|
|
6914
|
+
if (localIds.has(row.conversationId) || !row.startedAt) {
|
|
6915
|
+
continue;
|
|
6916
|
+
}
|
|
6917
|
+
const sessionId = row.conversationId;
|
|
6918
|
+
const model = row.model;
|
|
6919
|
+
const endedAt = row.ts || row.startedAt;
|
|
6920
|
+
const push = (partial, topType) => {
|
|
6921
|
+
lineNumber += 1;
|
|
6922
|
+
const event = {
|
|
6923
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6924
|
+
source: SOURCE_ID,
|
|
6925
|
+
agent: AGENT_NAME,
|
|
6926
|
+
workspaceId,
|
|
6927
|
+
project,
|
|
6928
|
+
model,
|
|
6929
|
+
sessionId,
|
|
6930
|
+
...partial
|
|
6931
|
+
};
|
|
6932
|
+
events.push(withBackfillRefs(event, {
|
|
6933
|
+
filePath,
|
|
6934
|
+
sourcePathHash,
|
|
6935
|
+
lineNumber,
|
|
6936
|
+
topType,
|
|
6937
|
+
payloadType: event.type,
|
|
6938
|
+
options
|
|
6939
|
+
}));
|
|
6940
|
+
};
|
|
6941
|
+
push({
|
|
6942
|
+
ts: row.startedAt,
|
|
6943
|
+
type: "session.started",
|
|
6944
|
+
confidence: "partial",
|
|
6945
|
+
refs: stringRefs({ sourceId: `${sessionId}:started` })
|
|
6946
|
+
}, "cloud-agent");
|
|
6947
|
+
emitPersistedCursorUsage(
|
|
6948
|
+
push,
|
|
6949
|
+
[cloudUsageToPersisted(row)],
|
|
6950
|
+
endedAt,
|
|
6951
|
+
void 0,
|
|
6952
|
+
model,
|
|
6953
|
+
sessionId
|
|
6954
|
+
);
|
|
6955
|
+
push({
|
|
6956
|
+
ts: endedAt,
|
|
6957
|
+
type: "session.ended",
|
|
6958
|
+
confidence: "partial",
|
|
6959
|
+
refs: stringRefs({ sourceId: `${sessionId}:ended` })
|
|
6960
|
+
}, "cloud-agent");
|
|
6961
|
+
}
|
|
6962
|
+
return events;
|
|
6963
|
+
}
|
|
6964
|
+
async function parseCursorCloudAgentFile(filePath, options) {
|
|
6965
|
+
const home = stringOption(options.home) || os6.homedir();
|
|
6966
|
+
const env = {
|
|
6967
|
+
CURSOR_HOME: process.env.CURSOR_HOME,
|
|
6968
|
+
CURSOR_USER_DIR: process.env.CURSOR_USER_DIR
|
|
6969
|
+
};
|
|
6970
|
+
const injected = injectedCursorCloudUsage(options);
|
|
6971
|
+
const sessions = injected ? [...injected.entries()].flatMap(([conversationId, events]) => {
|
|
6972
|
+
const summed = sumCursorCloudEvents(conversationId, events);
|
|
6973
|
+
return summed ? [summed] : [];
|
|
6974
|
+
}) : parseCursorCloudUsageCache(await readJsonIfExists(filePath));
|
|
6975
|
+
const localIds = await listLocalCursorSessionIds(home, env);
|
|
6976
|
+
return parseCursorCloudAgentSessions(filePath, options, sessions, localIds);
|
|
6977
|
+
}
|
|
6416
6978
|
function decodeKv(value) {
|
|
6417
6979
|
if (typeof value === "string") {
|
|
6418
6980
|
return value;
|
|
@@ -6778,7 +7340,7 @@ async function buildCursorChatMetaIndex(cursorHome2) {
|
|
|
6778
7340
|
return index;
|
|
6779
7341
|
}
|
|
6780
7342
|
async function readCursorStoreMeta(storePath) {
|
|
6781
|
-
const info = await
|
|
7343
|
+
const info = await stat8(storePath).catch(() => null);
|
|
6782
7344
|
if (!info) {
|
|
6783
7345
|
return {};
|
|
6784
7346
|
}
|
|
@@ -6851,7 +7413,7 @@ async function parseCursorTranscriptFile(filePath, options) {
|
|
|
6851
7413
|
}
|
|
6852
7414
|
const sessionId = path13.basename(filePath, ".jsonl");
|
|
6853
7415
|
const context = await resolveTranscriptContext(filePath, sessionId);
|
|
6854
|
-
const fileInfo = await
|
|
7416
|
+
const fileInfo = await stat8(filePath).catch(() => null);
|
|
6855
7417
|
let cwd = context.cwd;
|
|
6856
7418
|
let project = context.project;
|
|
6857
7419
|
let model = context.model;
|
|
@@ -7062,6 +7624,7 @@ async function parseCursorTranscriptFile(filePath, options) {
|
|
|
7062
7624
|
}
|
|
7063
7625
|
}
|
|
7064
7626
|
const endedAt = latestTimestamp(lastTs, context.endedAt, fileMtime);
|
|
7627
|
+
const lastTurnId = currentTurnId;
|
|
7065
7628
|
if (endedAt) {
|
|
7066
7629
|
closeTurn(endedAt);
|
|
7067
7630
|
if (sessionStarted) {
|
|
@@ -7073,11 +7636,60 @@ async function parseCursorTranscriptFile(filePath, options) {
|
|
|
7073
7636
|
}, "transcript");
|
|
7074
7637
|
}
|
|
7075
7638
|
}
|
|
7639
|
+
await appendPersistedCursorUsage({
|
|
7640
|
+
push,
|
|
7641
|
+
sessionId,
|
|
7642
|
+
options,
|
|
7643
|
+
filePath,
|
|
7644
|
+
fallbackTs: endedAt || lastTs,
|
|
7645
|
+
lastTurnId,
|
|
7646
|
+
model,
|
|
7647
|
+
skip: events.some((event) => event.type === "model.usage")
|
|
7648
|
+
});
|
|
7076
7649
|
return events;
|
|
7077
7650
|
}
|
|
7651
|
+
async function appendPersistedCursorUsage(args) {
|
|
7652
|
+
if (args.skip) {
|
|
7653
|
+
return;
|
|
7654
|
+
}
|
|
7655
|
+
emitPersistedCursorUsage(
|
|
7656
|
+
args.push,
|
|
7657
|
+
await resolveCursorSessionUsage(args.options, args.sessionId, args.filePath),
|
|
7658
|
+
args.fallbackTs,
|
|
7659
|
+
args.lastTurnId,
|
|
7660
|
+
args.model,
|
|
7661
|
+
args.sessionId
|
|
7662
|
+
);
|
|
7663
|
+
}
|
|
7664
|
+
function emitPersistedCursorUsage(push, usage, fallbackTs, lastTurnId, model, sessionId, skip = false) {
|
|
7665
|
+
if (skip || !usage?.length || !fallbackTs) {
|
|
7666
|
+
return;
|
|
7667
|
+
}
|
|
7668
|
+
for (const item of usage) {
|
|
7669
|
+
const metrics = usageMetrics({
|
|
7670
|
+
inputTokens: item.tokensInput,
|
|
7671
|
+
outputTokens: item.tokensOutput,
|
|
7672
|
+
cacheReadTokens: item.tokensCacheReadInput,
|
|
7673
|
+
cacheWriteTokens: item.tokensCacheCreationInput
|
|
7674
|
+
});
|
|
7675
|
+
if (!metrics) {
|
|
7676
|
+
continue;
|
|
7677
|
+
}
|
|
7678
|
+
const fromCloud = item.generationId.startsWith(CURSOR_CLOUD_USAGE_GENERATION_ID);
|
|
7679
|
+
push({
|
|
7680
|
+
ts: timestampFrom(item.ts) || fallbackTs,
|
|
7681
|
+
type: "model.usage",
|
|
7682
|
+
turnId: lastTurnId,
|
|
7683
|
+
model: item.model || model,
|
|
7684
|
+
confidence: fromCloud ? "partial" : "exact",
|
|
7685
|
+
metrics,
|
|
7686
|
+
refs: stringRefs({ sourceId: `${sessionId}:${item.generationId}:usage` })
|
|
7687
|
+
}, fromCloud ? "cloud-usage" : "hook-usage");
|
|
7688
|
+
}
|
|
7689
|
+
}
|
|
7078
7690
|
async function listCursorComposerIds(dbPath) {
|
|
7079
7691
|
const ids = /* @__PURE__ */ new Set();
|
|
7080
|
-
const info = await
|
|
7692
|
+
const info = await stat8(dbPath).catch(() => null);
|
|
7081
7693
|
if (!info) {
|
|
7082
7694
|
return ids;
|
|
7083
7695
|
}
|
|
@@ -7096,7 +7708,17 @@ async function listCursorComposerIds(dbPath) {
|
|
|
7096
7708
|
}
|
|
7097
7709
|
return ids;
|
|
7098
7710
|
}
|
|
7099
|
-
async function
|
|
7711
|
+
async function modifiedAtWithHookUsage(fileMtime, home, sessionIds) {
|
|
7712
|
+
let latest = fileMtime.getTime();
|
|
7713
|
+
for (const sessionId of sessionIds) {
|
|
7714
|
+
const extra = await persistedSessionContextModifiedAt(home, sessionId);
|
|
7715
|
+
if (extra && extra.getTime() > latest) {
|
|
7716
|
+
latest = extra.getTime();
|
|
7717
|
+
}
|
|
7718
|
+
}
|
|
7719
|
+
return new Date(latest).toISOString();
|
|
7720
|
+
}
|
|
7721
|
+
async function collectCursorTranscriptFiles(root, home, skipSessionIds) {
|
|
7100
7722
|
const files = [];
|
|
7101
7723
|
const seen = /* @__PURE__ */ new Set();
|
|
7102
7724
|
for (const filePath of await listJsonlFiles(root)) {
|
|
@@ -7108,16 +7730,22 @@ async function collectCursorTranscriptFiles(root, skipSessionIds) {
|
|
|
7108
7730
|
continue;
|
|
7109
7731
|
}
|
|
7110
7732
|
seen.add(sessionId);
|
|
7111
|
-
const info = await
|
|
7733
|
+
const info = await stat8(filePath).catch(() => null);
|
|
7112
7734
|
if (!info) {
|
|
7113
7735
|
continue;
|
|
7114
7736
|
}
|
|
7115
|
-
files.push({
|
|
7737
|
+
files.push({
|
|
7738
|
+
path: filePath,
|
|
7739
|
+
modifiedAt: await modifiedAtWithHookUsage(info.mtime, home, [sessionId])
|
|
7740
|
+
});
|
|
7116
7741
|
}
|
|
7117
7742
|
return files;
|
|
7118
7743
|
}
|
|
7119
7744
|
async function parseCursorSessionFile(filePath, options) {
|
|
7120
7745
|
const base = path13.basename(filePath);
|
|
7746
|
+
if (base === CURSOR_CLOUD_USAGE_FILENAME) {
|
|
7747
|
+
return parseCursorCloudAgentFile(filePath, options);
|
|
7748
|
+
}
|
|
7121
7749
|
if (base.endsWith(".jsonl")) {
|
|
7122
7750
|
return parseCursorTranscriptFile(filePath, options);
|
|
7123
7751
|
}
|
|
@@ -7133,7 +7761,8 @@ async function parseCursorSessionFile(filePath, options) {
|
|
|
7133
7761
|
try {
|
|
7134
7762
|
const composers = listComposers(opened.db);
|
|
7135
7763
|
for (const composer of composers) {
|
|
7136
|
-
|
|
7764
|
+
const usage = await resolveCursorSessionUsage(options, composer.composerId, filePath);
|
|
7765
|
+
events.push(...parseComposer(opened.db, composer, filePath, sourcePathHash, options, usage));
|
|
7137
7766
|
}
|
|
7138
7767
|
} finally {
|
|
7139
7768
|
opened.db.close();
|
|
@@ -7141,7 +7770,7 @@ async function parseCursorSessionFile(filePath, options) {
|
|
|
7141
7770
|
}
|
|
7142
7771
|
return events;
|
|
7143
7772
|
}
|
|
7144
|
-
function parseComposer(db, composer, filePath, sourcePathHash, options) {
|
|
7773
|
+
function parseComposer(db, composer, filePath, sourcePathHash, options, persistedUsage) {
|
|
7145
7774
|
const sessionId = composer.composerId;
|
|
7146
7775
|
const data = composer.data;
|
|
7147
7776
|
const { cwd, project } = workspaceFromComposer(data);
|
|
@@ -7351,54 +7980,79 @@ function parseComposer(db, composer, filePath, sourcePathHash, options) {
|
|
|
7351
7980
|
refs: stringRefs({ sourceId: `${sessionId}:ended` })
|
|
7352
7981
|
}, "composer");
|
|
7353
7982
|
}
|
|
7983
|
+
emitPersistedCursorUsage(push, persistedUsage, endedAt || startedAt, currentTurnId, model, sessionId, events.some((event) => event.type === "model.usage"));
|
|
7354
7984
|
return events;
|
|
7355
7985
|
}
|
|
7356
|
-
async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env) {
|
|
7986
|
+
async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env, options) {
|
|
7357
7987
|
if (sourceRoot) {
|
|
7358
|
-
const info = await
|
|
7988
|
+
const info = await stat8(sourceRoot).catch(() => null);
|
|
7359
7989
|
if (!info) {
|
|
7360
7990
|
return [];
|
|
7361
7991
|
}
|
|
7362
7992
|
if (info.isDirectory()) {
|
|
7363
7993
|
const files2 = [];
|
|
7994
|
+
const skipIds2 = /* @__PURE__ */ new Set();
|
|
7364
7995
|
for (const dbPath of await listFilesByExtensions(sourceRoot, [".vscdb", ".db"])) {
|
|
7365
7996
|
const base = path13.basename(dbPath);
|
|
7366
7997
|
if (base === "store.db" || base !== "state.vscdb" && !base.endsWith(".vscdb")) {
|
|
7367
7998
|
continue;
|
|
7368
7999
|
}
|
|
7369
|
-
const dbInfo = await
|
|
7370
|
-
if (dbInfo) {
|
|
7371
|
-
|
|
8000
|
+
const dbInfo = await stat8(dbPath).catch(() => null);
|
|
8001
|
+
if (!dbInfo) {
|
|
8002
|
+
continue;
|
|
7372
8003
|
}
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
for (const file of files2) {
|
|
7376
|
-
for (const id of await listCursorComposerIds(file.path)) {
|
|
8004
|
+
const composerIds2 = await listCursorComposerIds(dbPath);
|
|
8005
|
+
for (const id of composerIds2) {
|
|
7377
8006
|
skipIds2.add(id);
|
|
7378
8007
|
}
|
|
8008
|
+
files2.push({
|
|
8009
|
+
path: dbPath,
|
|
8010
|
+
modifiedAt: await modifiedAtWithHookUsage(dbInfo.mtime, home, composerIds2)
|
|
8011
|
+
});
|
|
7379
8012
|
}
|
|
7380
|
-
files2.push(...await collectCursorTranscriptFiles(sourceRoot, skipIds2));
|
|
8013
|
+
files2.push(...await collectCursorTranscriptFiles(sourceRoot, home, skipIds2));
|
|
7381
8014
|
return files2;
|
|
7382
8015
|
}
|
|
7383
8016
|
if (path13.basename(sourceRoot) === "store.db") {
|
|
7384
8017
|
return [];
|
|
7385
8018
|
}
|
|
7386
|
-
|
|
8019
|
+
if (sourceRoot.endsWith(".jsonl")) {
|
|
8020
|
+
return [{
|
|
8021
|
+
path: sourceRoot,
|
|
8022
|
+
modifiedAt: await modifiedAtWithHookUsage(
|
|
8023
|
+
info.mtime,
|
|
8024
|
+
home,
|
|
8025
|
+
[path13.basename(sourceRoot, ".jsonl")]
|
|
8026
|
+
)
|
|
8027
|
+
}];
|
|
8028
|
+
}
|
|
8029
|
+
const composerIds = await listCursorComposerIds(sourceRoot);
|
|
8030
|
+
return [{
|
|
8031
|
+
path: sourceRoot,
|
|
8032
|
+
modifiedAt: await modifiedAtWithHookUsage(info.mtime, home, composerIds)
|
|
8033
|
+
}];
|
|
7387
8034
|
}
|
|
7388
8035
|
const files = [];
|
|
7389
8036
|
const skipIds = /* @__PURE__ */ new Set();
|
|
7390
8037
|
for (const candidatePath of cursorStateDbCandidates(home, env)) {
|
|
7391
|
-
const info = await
|
|
8038
|
+
const info = await stat8(candidatePath).catch(() => null);
|
|
7392
8039
|
if (!info) {
|
|
7393
8040
|
continue;
|
|
7394
8041
|
}
|
|
7395
|
-
|
|
7396
|
-
for (const id of
|
|
8042
|
+
const composerIds = await listCursorComposerIds(candidatePath);
|
|
8043
|
+
for (const id of composerIds) {
|
|
7397
8044
|
skipIds.add(id);
|
|
7398
8045
|
}
|
|
8046
|
+
files.push({
|
|
8047
|
+
path: candidatePath,
|
|
8048
|
+
modifiedAt: await modifiedAtWithHookUsage(info.mtime, home, composerIds)
|
|
8049
|
+
});
|
|
7399
8050
|
break;
|
|
7400
8051
|
}
|
|
7401
|
-
files.push(...await collectCursorTranscriptFiles(cursorProjectsDir(home, env), skipIds));
|
|
8052
|
+
files.push(...await collectCursorTranscriptFiles(cursorProjectsDir(home, env), home, skipIds));
|
|
8053
|
+
if (options) {
|
|
8054
|
+
await appendCursorCloudAgentSource(files, home, env, options);
|
|
8055
|
+
}
|
|
7402
8056
|
return files;
|
|
7403
8057
|
}
|
|
7404
8058
|
function cursorHookConfig() {
|
|
@@ -7444,7 +8098,7 @@ function createCursorAdapter() {
|
|
|
7444
8098
|
}
|
|
7445
8099
|
|
|
7446
8100
|
// src/adapters/grok-build.ts
|
|
7447
|
-
import { readdir as readdir7, readFile as readFile9, stat as
|
|
8101
|
+
import { readdir as readdir7, readFile as readFile9, stat as stat9 } from "node:fs/promises";
|
|
7448
8102
|
import path14 from "node:path";
|
|
7449
8103
|
init_fs();
|
|
7450
8104
|
var GROK_COST_TICKS_PER_USD = 1e9;
|
|
@@ -7484,7 +8138,7 @@ async function grokBackfillFiles(sourceRoot, home, env) {
|
|
|
7484
8138
|
continue;
|
|
7485
8139
|
}
|
|
7486
8140
|
try {
|
|
7487
|
-
const info = await
|
|
8141
|
+
const info = await stat9(entryPath);
|
|
7488
8142
|
files.push({ path: entryPath, modifiedAt: info.mtime.toISOString() });
|
|
7489
8143
|
} catch {
|
|
7490
8144
|
}
|
|
@@ -9244,19 +9898,19 @@ function opencodeDataCandidates(home, env) {
|
|
|
9244
9898
|
return [primary, path16.join(home, ".opencode", "opencode.db")];
|
|
9245
9899
|
}
|
|
9246
9900
|
async function opencodeBackfillFiles(sourceRoot, home = os7.homedir(), env) {
|
|
9247
|
-
const { stat:
|
|
9901
|
+
const { stat: stat17 } = await import("node:fs/promises");
|
|
9248
9902
|
if (sourceRoot) {
|
|
9249
9903
|
if (!sourceRoot.endsWith(".db")) {
|
|
9250
9904
|
return [];
|
|
9251
9905
|
}
|
|
9252
|
-
const info = await
|
|
9906
|
+
const info = await stat17(sourceRoot).catch(() => null);
|
|
9253
9907
|
if (!info) {
|
|
9254
9908
|
return [];
|
|
9255
9909
|
}
|
|
9256
9910
|
return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
|
|
9257
9911
|
}
|
|
9258
9912
|
for (const candidatePath of opencodeDataCandidates(home, env)) {
|
|
9259
|
-
const info = await
|
|
9913
|
+
const info = await stat17(candidatePath).catch(() => null);
|
|
9260
9914
|
if (info) {
|
|
9261
9915
|
return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
|
|
9262
9916
|
}
|
|
@@ -9356,7 +10010,7 @@ function createOpenCodeAdapter() {
|
|
|
9356
10010
|
}
|
|
9357
10011
|
|
|
9358
10012
|
// src/adapters/pi.ts
|
|
9359
|
-
import { readdir as readdir8, readFile as readFile11, stat as
|
|
10013
|
+
import { readdir as readdir8, readFile as readFile11, stat as stat10 } from "node:fs/promises";
|
|
9360
10014
|
import os8 from "node:os";
|
|
9361
10015
|
import path17 from "node:path";
|
|
9362
10016
|
init_fs();
|
|
@@ -10052,7 +10706,7 @@ async function piBackfillFiles(sourceRoot, home = os8.homedir(), env) {
|
|
|
10052
10706
|
]);
|
|
10053
10707
|
const files = lists.flat().sort();
|
|
10054
10708
|
return Promise.all(files.map(async (filePath) => {
|
|
10055
|
-
const info = await
|
|
10709
|
+
const info = await stat10(filePath);
|
|
10056
10710
|
let groupId = resolvePiBackfillGroupId(filePath);
|
|
10057
10711
|
if (!groupId && filePath.endsWith(".json")) {
|
|
10058
10712
|
groupId = await workflowRunGroupId(filePath);
|
|
@@ -10099,7 +10753,7 @@ function createPiAdapter() {
|
|
|
10099
10753
|
}
|
|
10100
10754
|
|
|
10101
10755
|
// src/adapters/qoder-cn.ts
|
|
10102
|
-
import { readdir as readdir9, readFile as readFile12, stat as
|
|
10756
|
+
import { readdir as readdir9, readFile as readFile12, stat as stat11 } from "node:fs/promises";
|
|
10103
10757
|
import os10 from "node:os";
|
|
10104
10758
|
import path19 from "node:path";
|
|
10105
10759
|
|
|
@@ -10996,7 +11650,7 @@ async function gitRootFromCwds2(cwds) {
|
|
|
10996
11650
|
while (!seen.has(current)) {
|
|
10997
11651
|
seen.add(current);
|
|
10998
11652
|
try {
|
|
10999
|
-
await
|
|
11653
|
+
await stat11(path19.join(current, ".git"));
|
|
11000
11654
|
return current;
|
|
11001
11655
|
} catch {
|
|
11002
11656
|
}
|
|
@@ -11146,7 +11800,7 @@ function createQoderCnAdapter() {
|
|
|
11146
11800
|
|
|
11147
11801
|
// src/adapters/qoder.ts
|
|
11148
11802
|
import { existsSync } from "node:fs";
|
|
11149
|
-
import { readdir as readdir10, readFile as readFile13, stat as
|
|
11803
|
+
import { readdir as readdir10, readFile as readFile13, stat as stat12 } from "node:fs/promises";
|
|
11150
11804
|
import os11 from "node:os";
|
|
11151
11805
|
import path20 from "node:path";
|
|
11152
11806
|
function parseQoderPaths(filePath) {
|
|
@@ -11842,7 +12496,7 @@ async function gitRootFromCwds3(cwds) {
|
|
|
11842
12496
|
while (!seen.has(current)) {
|
|
11843
12497
|
seen.add(current);
|
|
11844
12498
|
try {
|
|
11845
|
-
await
|
|
12499
|
+
await stat12(path20.join(current, ".git"));
|
|
11846
12500
|
return current;
|
|
11847
12501
|
} catch {
|
|
11848
12502
|
}
|
|
@@ -12050,7 +12704,7 @@ function normalizeId(id) {
|
|
|
12050
12704
|
}
|
|
12051
12705
|
|
|
12052
12706
|
// src/adapters/workbuddy.ts
|
|
12053
|
-
import { readdir as readdir11, readFile as readFile14, stat as
|
|
12707
|
+
import { readdir as readdir11, readFile as readFile14, stat as stat13 } from "node:fs/promises";
|
|
12054
12708
|
import path21 from "node:path";
|
|
12055
12709
|
function workbuddyProjectsDir(home, env) {
|
|
12056
12710
|
const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
|
|
@@ -12503,7 +13157,7 @@ async function workbuddyBackfillFiles(sourceRoot, home, env) {
|
|
|
12503
13157
|
for (const entry of entries) {
|
|
12504
13158
|
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
12505
13159
|
const filePath = path21.join(projectDir, entry.name);
|
|
12506
|
-
const info = await
|
|
13160
|
+
const info = await stat13(filePath);
|
|
12507
13161
|
files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
|
|
12508
13162
|
}
|
|
12509
13163
|
}
|
|
@@ -12564,7 +13218,7 @@ function createWorkbuddyAdapter() {
|
|
|
12564
13218
|
|
|
12565
13219
|
// src/adapters/zcode.ts
|
|
12566
13220
|
import { execFile } from "node:child_process";
|
|
12567
|
-
import { readFile as readFile15, stat as
|
|
13221
|
+
import { readFile as readFile15, stat as stat14 } from "node:fs/promises";
|
|
12568
13222
|
import path22 from "node:path";
|
|
12569
13223
|
import { promisify as promisify2 } from "node:util";
|
|
12570
13224
|
init_fs();
|
|
@@ -12583,7 +13237,7 @@ var providerNameCache = null;
|
|
|
12583
13237
|
async function loadProviderNames(configPath2) {
|
|
12584
13238
|
let fileMtime = 0;
|
|
12585
13239
|
try {
|
|
12586
|
-
const info = await
|
|
13240
|
+
const info = await stat14(configPath2);
|
|
12587
13241
|
fileMtime = info.mtimeMs;
|
|
12588
13242
|
} catch {
|
|
12589
13243
|
return /* @__PURE__ */ new Map();
|
|
@@ -12800,7 +13454,7 @@ async function parseZCodeDb(filePath, options) {
|
|
|
12800
13454
|
for (let i = 0; i < 12; i++) {
|
|
12801
13455
|
const probe = path22.join(candidate, ".zcode", "v2", "config.json");
|
|
12802
13456
|
try {
|
|
12803
|
-
await
|
|
13457
|
+
await stat14(probe);
|
|
12804
13458
|
configPath2 = probe;
|
|
12805
13459
|
break;
|
|
12806
13460
|
} catch {
|
|
@@ -13026,7 +13680,7 @@ async function zcodeBackfillFiles(sourceRoot, home, env) {
|
|
|
13026
13680
|
const candidate = sourceRoot || zcodeDbPath(home, env);
|
|
13027
13681
|
const filePath = candidate.endsWith(".sqlite") ? candidate : path22.join(candidate, "db", "db.sqlite");
|
|
13028
13682
|
try {
|
|
13029
|
-
const info = await
|
|
13683
|
+
const info = await stat14(filePath);
|
|
13030
13684
|
return [{ path: filePath, modifiedAt: info.mtime.toISOString() }];
|
|
13031
13685
|
} catch {
|
|
13032
13686
|
return [];
|
|
@@ -13411,19 +14065,19 @@ async function parseZedSessionFile(dbPath, options) {
|
|
|
13411
14065
|
return events.filter((event) => matchesBackfillFilters(event, options));
|
|
13412
14066
|
}
|
|
13413
14067
|
async function zedBackfillFiles(sourceRoot, home = os12.homedir(), env) {
|
|
13414
|
-
const { stat:
|
|
14068
|
+
const { stat: stat17 } = await import("node:fs/promises");
|
|
13415
14069
|
if (sourceRoot) {
|
|
13416
14070
|
if (!sourceRoot.endsWith(".db")) {
|
|
13417
14071
|
return [];
|
|
13418
14072
|
}
|
|
13419
|
-
const info = await
|
|
14073
|
+
const info = await stat17(sourceRoot).catch(() => null);
|
|
13420
14074
|
if (!info) {
|
|
13421
14075
|
return [];
|
|
13422
14076
|
}
|
|
13423
14077
|
return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
|
|
13424
14078
|
}
|
|
13425
14079
|
for (const candidatePath of zedThreadsCandidates(home, env)) {
|
|
13426
|
-
const info = await
|
|
14080
|
+
const info = await stat17(candidatePath).catch(() => null);
|
|
13427
14081
|
if (info) {
|
|
13428
14082
|
return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
|
|
13429
14083
|
}
|
|
@@ -13844,7 +14498,7 @@ async function uninstallEntry(entry, options) {
|
|
|
13844
14498
|
await uninstallGeneratedFile(entry.path, options);
|
|
13845
14499
|
}
|
|
13846
14500
|
async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
|
|
13847
|
-
const { mkdir:
|
|
14501
|
+
const { mkdir: mkdir7, writeFile: writeFile6 } = await import("node:fs/promises");
|
|
13848
14502
|
const pathMod = await import("node:path");
|
|
13849
14503
|
if (dryRun) {
|
|
13850
14504
|
onWrite(`Would merge ${filePath}`);
|
|
@@ -13865,12 +14519,12 @@ async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
|
|
|
13865
14519
|
onWrite(`Already installed ${filePath}`);
|
|
13866
14520
|
return;
|
|
13867
14521
|
}
|
|
13868
|
-
await
|
|
13869
|
-
await
|
|
14522
|
+
await mkdir7(pathMod.dirname(filePath), { recursive: true });
|
|
14523
|
+
await writeFile6(filePath, nextText, "utf8");
|
|
13870
14524
|
onWrite(`Installed ${filePath}`);
|
|
13871
14525
|
}
|
|
13872
14526
|
async function mergeCursorHooksFile(filePath, content, { dryRun, force, onWrite }) {
|
|
13873
|
-
const { mkdir:
|
|
14527
|
+
const { mkdir: mkdir7, writeFile: writeFile6 } = await import("node:fs/promises");
|
|
13874
14528
|
const pathMod = await import("node:path");
|
|
13875
14529
|
if (dryRun) {
|
|
13876
14530
|
onWrite(`Would merge ${filePath}`);
|
|
@@ -13890,8 +14544,8 @@ async function mergeCursorHooksFile(filePath, content, { dryRun, force, onWrite
|
|
|
13890
14544
|
onWrite(`Already installed ${filePath}`);
|
|
13891
14545
|
return;
|
|
13892
14546
|
}
|
|
13893
|
-
await
|
|
13894
|
-
await
|
|
14547
|
+
await mkdir7(pathMod.dirname(filePath), { recursive: true });
|
|
14548
|
+
await writeFile6(filePath, nextText, "utf8");
|
|
13895
14549
|
onWrite(`Installed ${filePath}`);
|
|
13896
14550
|
}
|
|
13897
14551
|
async function uninstallCursorHooksFile(filePath, content, { dryRun, onWrite }) {
|
|
@@ -13925,8 +14579,8 @@ async function uninstallCursorHooksFile(filePath, content, { dryRun, onWrite })
|
|
|
13925
14579
|
onWrite(`Would uninstall ${filePath}`);
|
|
13926
14580
|
return;
|
|
13927
14581
|
}
|
|
13928
|
-
const { writeFile:
|
|
13929
|
-
await
|
|
14582
|
+
const { writeFile: writeFile6 } = await import("node:fs/promises");
|
|
14583
|
+
await writeFile6(filePath, `${JSON.stringify(next, null, 2)}
|
|
13930
14584
|
`, "utf8");
|
|
13931
14585
|
onWrite(`Uninstalled ${filePath}`);
|
|
13932
14586
|
}
|
|
@@ -14001,7 +14655,7 @@ function hookCommandFromGroup(group) {
|
|
|
14001
14655
|
return isPlainObject(hook) && typeof hook.command === "string" ? hook.command : void 0;
|
|
14002
14656
|
}
|
|
14003
14657
|
async function mergeHooksToml(filePath, content, { dryRun, force, onWrite }) {
|
|
14004
|
-
const { mkdir:
|
|
14658
|
+
const { mkdir: mkdir7, writeFile: writeFile6 } = await import("node:fs/promises");
|
|
14005
14659
|
const pathMod = await import("node:path");
|
|
14006
14660
|
if (dryRun) {
|
|
14007
14661
|
onWrite(`Would merge ${filePath}`);
|
|
@@ -14029,8 +14683,8 @@ async function mergeHooksToml(filePath, content, { dryRun, force, onWrite }) {
|
|
|
14029
14683
|
return;
|
|
14030
14684
|
}
|
|
14031
14685
|
}
|
|
14032
|
-
await
|
|
14033
|
-
await
|
|
14686
|
+
await mkdir7(pathMod.dirname(filePath), { recursive: true });
|
|
14687
|
+
await writeFile6(filePath, nextText, "utf8");
|
|
14034
14688
|
onWrite(`Installed ${filePath}`);
|
|
14035
14689
|
}
|
|
14036
14690
|
async function uninstallHooksToml(filePath, content, { dryRun, onWrite }) {
|
|
@@ -14055,8 +14709,8 @@ async function uninstallHooksToml(filePath, content, { dryRun, onWrite }) {
|
|
|
14055
14709
|
onWrite(`Would uninstall ${filePath}`);
|
|
14056
14710
|
return;
|
|
14057
14711
|
}
|
|
14058
|
-
const { writeFile:
|
|
14059
|
-
await
|
|
14712
|
+
const { writeFile: writeFile6 } = await import("node:fs/promises");
|
|
14713
|
+
await writeFile6(filePath, nextText, "utf8");
|
|
14060
14714
|
onWrite(`Uninstalled ${filePath}`);
|
|
14061
14715
|
}
|
|
14062
14716
|
function collectHookCommandsFromJsonContent(content) {
|
|
@@ -14189,8 +14843,8 @@ async function uninstallHooksJson(filePath, content, { dryRun, onWrite }) {
|
|
|
14189
14843
|
onWrite(`Would uninstall ${filePath}`);
|
|
14190
14844
|
return;
|
|
14191
14845
|
}
|
|
14192
|
-
const { writeFile:
|
|
14193
|
-
await
|
|
14846
|
+
const { writeFile: writeFile6 } = await import("node:fs/promises");
|
|
14847
|
+
await writeFile6(filePath, `${JSON.stringify(next, null, 2)}
|
|
14194
14848
|
`, "utf8");
|
|
14195
14849
|
onWrite(`Uninstalled ${filePath}`);
|
|
14196
14850
|
}
|
|
@@ -14272,7 +14926,7 @@ function defaultMachineName() {
|
|
|
14272
14926
|
init_fs();
|
|
14273
14927
|
|
|
14274
14928
|
// src/lib/logger.ts
|
|
14275
|
-
import { appendFile, mkdir as
|
|
14929
|
+
import { appendFile, mkdir as mkdir5, rename, stat as stat15 } from "node:fs/promises";
|
|
14276
14930
|
import { homedir as homedir2 } from "node:os";
|
|
14277
14931
|
import path25 from "node:path";
|
|
14278
14932
|
var MAX_BYTES = 1 * 1024 * 1024;
|
|
@@ -14290,7 +14944,7 @@ function serializeError(error) {
|
|
|
14290
14944
|
}
|
|
14291
14945
|
async function rotateIfNeeded(file) {
|
|
14292
14946
|
try {
|
|
14293
|
-
const info = await
|
|
14947
|
+
const info = await stat15(file);
|
|
14294
14948
|
if (info.size > MAX_BYTES) {
|
|
14295
14949
|
await rename(file, `${file}.1`).catch(() => {
|
|
14296
14950
|
});
|
|
@@ -14301,7 +14955,7 @@ async function rotateIfNeeded(file) {
|
|
|
14301
14955
|
async function writeLog(entry, home = homedir2(), fileName = "cli.log") {
|
|
14302
14956
|
try {
|
|
14303
14957
|
const dir = logDir(home);
|
|
14304
|
-
await
|
|
14958
|
+
await mkdir5(dir, { recursive: true });
|
|
14305
14959
|
const file = logPath(home, fileName);
|
|
14306
14960
|
await rotateIfNeeded(file);
|
|
14307
14961
|
const record = {
|
|
@@ -14526,7 +15180,7 @@ async function deleteMachine(remote, id) {
|
|
|
14526
15180
|
}
|
|
14527
15181
|
|
|
14528
15182
|
// src/lib/types.ts
|
|
14529
|
-
var BACKFILL_STATE_SCHEMA_VERSION =
|
|
15183
|
+
var BACKFILL_STATE_SCHEMA_VERSION = 9;
|
|
14530
15184
|
|
|
14531
15185
|
// src/cli.ts
|
|
14532
15186
|
function createRegistry() {
|
|
@@ -15105,7 +15759,7 @@ async function listBackfillSourceFiles(source, options, ctx) {
|
|
|
15105
15759
|
return zedBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
15106
15760
|
}
|
|
15107
15761
|
if (source.id === "cursor") {
|
|
15108
|
-
return cursorBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
15762
|
+
return cursorBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env, options);
|
|
15109
15763
|
}
|
|
15110
15764
|
if (source.id === "pi") {
|
|
15111
15765
|
return piBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
@@ -15114,7 +15768,7 @@ async function listBackfillSourceFiles(source, options, ctx) {
|
|
|
15114
15768
|
const fileLists = await Promise.all(roots.map((r) => listJsonlFiles(r)));
|
|
15115
15769
|
const files = fileLists.flat().sort().slice(0, numberOption(options.limit) || void 0);
|
|
15116
15770
|
return Promise.all(files.map(async (filePath) => {
|
|
15117
|
-
const info = await
|
|
15771
|
+
const info = await stat16(filePath);
|
|
15118
15772
|
return { path: filePath, modifiedAt: info.mtime.toISOString() };
|
|
15119
15773
|
}));
|
|
15120
15774
|
}
|
|
@@ -15514,8 +16168,8 @@ async function readBackfillIncrementalStateFile(home, ctx) {
|
|
|
15514
16168
|
}
|
|
15515
16169
|
async function writeBackfillIncrementalStateFile(home, file) {
|
|
15516
16170
|
const statePath = backfillIncrementalStatePath(home);
|
|
15517
|
-
await
|
|
15518
|
-
await
|
|
16171
|
+
await mkdir6(path26.dirname(statePath), { recursive: true });
|
|
16172
|
+
await writeFile5(statePath, `${JSON.stringify(file, null, 2)}
|
|
15519
16173
|
`, "utf8");
|
|
15520
16174
|
}
|
|
15521
16175
|
async function readBackfillIncrementalState(home, remoteKey, ctx) {
|
|
@@ -15563,8 +16217,8 @@ async function readSyncLocalTriggerState(statePath) {
|
|
|
15563
16217
|
return nextState;
|
|
15564
16218
|
}
|
|
15565
16219
|
async function writeSyncLocalTriggerState(statePath, state) {
|
|
15566
|
-
await
|
|
15567
|
-
await
|
|
16220
|
+
await mkdir6(path26.dirname(statePath), { recursive: true });
|
|
16221
|
+
await writeFile5(statePath, `${JSON.stringify(state, null, 2)}
|
|
15568
16222
|
`, "utf8");
|
|
15569
16223
|
}
|
|
15570
16224
|
async function readSyncLocalLock(lockPath) {
|
|
@@ -15578,12 +16232,12 @@ async function readSyncLocalLock(lockPath) {
|
|
|
15578
16232
|
return { pid: lock.pid, startedAt: lock.startedAt };
|
|
15579
16233
|
}
|
|
15580
16234
|
async function writeSyncLocalLock(lockPath, lock) {
|
|
15581
|
-
await
|
|
15582
|
-
await
|
|
16235
|
+
await mkdir6(path26.dirname(lockPath), { recursive: true });
|
|
16236
|
+
await writeFile5(lockPath, `${JSON.stringify(lock, null, 2)}
|
|
15583
16237
|
`, "utf8");
|
|
15584
16238
|
}
|
|
15585
16239
|
async function acquireSyncLocalLock(lockPath, lock) {
|
|
15586
|
-
await
|
|
16240
|
+
await mkdir6(path26.dirname(lockPath), { recursive: true });
|
|
15587
16241
|
try {
|
|
15588
16242
|
const handle = await open(lockPath, "wx");
|
|
15589
16243
|
try {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yhong91/vibetime",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.59",
|
|
5
5
|
"description": "vibetime CLI — install AI-agent hooks (Claude Code, Codex, OpenCode, Pi, Cursor) and report activity to vibetime.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"publishConfig": {
|