@yhong91/vibetime 0.1.58 → 0.1.60
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 +563 -38
- 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.60" : "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;
|
|
@@ -2065,8 +2065,15 @@ function parseJsonLine(line) {
|
|
|
2065
2065
|
}
|
|
2066
2066
|
}
|
|
2067
2067
|
function timestampFrom(value) {
|
|
2068
|
-
if (typeof value === "string"
|
|
2069
|
-
|
|
2068
|
+
if (typeof value === "string") {
|
|
2069
|
+
const trimmed = value.trim();
|
|
2070
|
+
if (/^\d+(\.\d+)?$/.test(trimmed)) {
|
|
2071
|
+
return timestampFrom(Number(trimmed));
|
|
2072
|
+
}
|
|
2073
|
+
if (!Number.isNaN(Date.parse(trimmed))) {
|
|
2074
|
+
return trimmed;
|
|
2075
|
+
}
|
|
2076
|
+
return void 0;
|
|
2070
2077
|
}
|
|
2071
2078
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2072
2079
|
return void 0;
|
|
@@ -6292,7 +6299,7 @@ function createCopilotAdapter() {
|
|
|
6292
6299
|
}
|
|
6293
6300
|
|
|
6294
6301
|
// src/adapters/cursor.ts
|
|
6295
|
-
import { copyFile, readdir as readdir6, readFile as readFile8, stat as stat8 } from "node:fs/promises";
|
|
6302
|
+
import { copyFile, mkdir as mkdir4, readdir as readdir6, readFile as readFile8, stat as stat8, writeFile as writeFile4 } from "node:fs/promises";
|
|
6296
6303
|
import os6 from "node:os";
|
|
6297
6304
|
import path13 from "node:path";
|
|
6298
6305
|
|
|
@@ -6400,6 +6407,226 @@ function collectCursorHookCommands(content) {
|
|
|
6400
6407
|
|
|
6401
6408
|
// src/adapters/cursor.ts
|
|
6402
6409
|
init_fs();
|
|
6410
|
+
|
|
6411
|
+
// src/adapters/cursor-cloud-usage.ts
|
|
6412
|
+
var CURSOR_CLOUD_USAGE_GENERATION_ID = "cursor-cloud";
|
|
6413
|
+
var CURSOR_CLOUD_USAGE_FILENAME = "cursor-cloud-usage.json";
|
|
6414
|
+
var CURSOR_CLOUD_AGENT_PROJECT = "Cloud Agent";
|
|
6415
|
+
var CURSOR_DASHBOARD_USAGE_URL = "https://cursor.com/api/dashboard/get-filtered-usage-events";
|
|
6416
|
+
var CURSOR_CLOUD_USAGE_CACHE_VERSION = 1;
|
|
6417
|
+
var DEFAULT_WINDOW_DAYS = 90;
|
|
6418
|
+
var PAGE_SIZE = 1e3;
|
|
6419
|
+
function cursorCloudUsageDisabled(env = process.env) {
|
|
6420
|
+
const value = env.VIBETIME_CURSOR_CLOUD_USAGE;
|
|
6421
|
+
return value === "0" || value === "false";
|
|
6422
|
+
}
|
|
6423
|
+
function cursorSessionCookie(accessToken) {
|
|
6424
|
+
const token = accessToken.trim();
|
|
6425
|
+
if (!token) {
|
|
6426
|
+
return void 0;
|
|
6427
|
+
}
|
|
6428
|
+
const payload = jwtPayload(token);
|
|
6429
|
+
const userId = String(payload?.sub || "").split("|").at(-1)?.trim();
|
|
6430
|
+
if (!userId) {
|
|
6431
|
+
return void 0;
|
|
6432
|
+
}
|
|
6433
|
+
return `WorkosCursorSessionToken=${userId}%3A%3A${token}`;
|
|
6434
|
+
}
|
|
6435
|
+
function cursorCloudEventFromDashboard(event) {
|
|
6436
|
+
if (!isPlainObject(event)) {
|
|
6437
|
+
return void 0;
|
|
6438
|
+
}
|
|
6439
|
+
const conversationId = stringField(event, "conversationId")?.trim();
|
|
6440
|
+
if (!conversationId) {
|
|
6441
|
+
return void 0;
|
|
6442
|
+
}
|
|
6443
|
+
const tokenUsage = objectField(event, "tokenUsage");
|
|
6444
|
+
const tokensInput = numberField(tokenUsage, "inputTokens") || 0;
|
|
6445
|
+
const tokensOutput = numberField(tokenUsage, "outputTokens") || 0;
|
|
6446
|
+
const tokensCacheReadInput = numberField(tokenUsage, "cacheReadTokens") || 0;
|
|
6447
|
+
const tokensCacheCreationInput = numberField(tokenUsage, "cacheWriteTokens") || 0;
|
|
6448
|
+
if (tokensInput <= 0 && tokensOutput <= 0 && tokensCacheReadInput <= 0 && tokensCacheCreationInput <= 0) {
|
|
6449
|
+
return void 0;
|
|
6450
|
+
}
|
|
6451
|
+
const ts = timestampFrom(event.timestamp);
|
|
6452
|
+
if (!ts) {
|
|
6453
|
+
return void 0;
|
|
6454
|
+
}
|
|
6455
|
+
return {
|
|
6456
|
+
conversationId,
|
|
6457
|
+
model: stringField(event, "model") || void 0,
|
|
6458
|
+
ts,
|
|
6459
|
+
tokensInput,
|
|
6460
|
+
tokensOutput,
|
|
6461
|
+
tokensCacheReadInput,
|
|
6462
|
+
tokensCacheCreationInput
|
|
6463
|
+
};
|
|
6464
|
+
}
|
|
6465
|
+
function groupCursorUsageEvents(events) {
|
|
6466
|
+
const byConversation = /* @__PURE__ */ new Map();
|
|
6467
|
+
for (const event of events) {
|
|
6468
|
+
const parsed = cursorCloudEventFromDashboard(event);
|
|
6469
|
+
if (!parsed) {
|
|
6470
|
+
continue;
|
|
6471
|
+
}
|
|
6472
|
+
const current = byConversation.get(parsed.conversationId);
|
|
6473
|
+
if (current) {
|
|
6474
|
+
current.push(parsed);
|
|
6475
|
+
} else {
|
|
6476
|
+
byConversation.set(parsed.conversationId, [parsed]);
|
|
6477
|
+
}
|
|
6478
|
+
}
|
|
6479
|
+
return byConversation;
|
|
6480
|
+
}
|
|
6481
|
+
function sumCursorCloudEvents(conversationId, events) {
|
|
6482
|
+
if (events.length === 0) {
|
|
6483
|
+
return void 0;
|
|
6484
|
+
}
|
|
6485
|
+
const first = events[0];
|
|
6486
|
+
const firstStart = first.startedAt || first.ts;
|
|
6487
|
+
const summed = {
|
|
6488
|
+
conversationId,
|
|
6489
|
+
model: first.model,
|
|
6490
|
+
ts: first.ts,
|
|
6491
|
+
startedAt: firstStart,
|
|
6492
|
+
tokensInput: 0,
|
|
6493
|
+
tokensOutput: 0,
|
|
6494
|
+
tokensCacheReadInput: 0,
|
|
6495
|
+
tokensCacheCreationInput: 0
|
|
6496
|
+
};
|
|
6497
|
+
for (const event of events) {
|
|
6498
|
+
summed.tokensInput += event.tokensInput;
|
|
6499
|
+
summed.tokensOutput += event.tokensOutput;
|
|
6500
|
+
summed.tokensCacheReadInput += event.tokensCacheReadInput;
|
|
6501
|
+
summed.tokensCacheCreationInput += event.tokensCacheCreationInput;
|
|
6502
|
+
const eventStart = event.startedAt || event.ts;
|
|
6503
|
+
if (Date.parse(eventStart) < Date.parse(summed.startedAt)) {
|
|
6504
|
+
summed.startedAt = eventStart;
|
|
6505
|
+
}
|
|
6506
|
+
if (Date.parse(event.ts) >= Date.parse(summed.ts)) {
|
|
6507
|
+
summed.ts = event.ts;
|
|
6508
|
+
if (event.model) {
|
|
6509
|
+
summed.model = event.model;
|
|
6510
|
+
}
|
|
6511
|
+
}
|
|
6512
|
+
}
|
|
6513
|
+
return summed;
|
|
6514
|
+
}
|
|
6515
|
+
function hookUncachedInputTokens(turn) {
|
|
6516
|
+
return Math.max(0, (turn.tokensInput || 0) - (turn.tokensCacheReadInput || 0));
|
|
6517
|
+
}
|
|
6518
|
+
function cloudEventMatchesHookTurn(event, turn) {
|
|
6519
|
+
return event.tokensInput === hookUncachedInputTokens(turn) && event.tokensOutput === (turn.tokensOutput || 0) && event.tokensCacheReadInput === (turn.tokensCacheReadInput || 0);
|
|
6520
|
+
}
|
|
6521
|
+
function unmatchedCloudEvents(events, turns) {
|
|
6522
|
+
const used = /* @__PURE__ */ new Set();
|
|
6523
|
+
for (const turn of turns) {
|
|
6524
|
+
const index = events.findIndex((event, i) => !used.has(i) && cloudEventMatchesHookTurn(event, turn));
|
|
6525
|
+
if (index >= 0) {
|
|
6526
|
+
used.add(index);
|
|
6527
|
+
}
|
|
6528
|
+
}
|
|
6529
|
+
return events.filter((_, i) => !used.has(i));
|
|
6530
|
+
}
|
|
6531
|
+
async function fetchCursorDashboardUsage(args) {
|
|
6532
|
+
const cookie = cursorSessionCookie(args.accessToken);
|
|
6533
|
+
if (!cookie) {
|
|
6534
|
+
return /* @__PURE__ */ new Map();
|
|
6535
|
+
}
|
|
6536
|
+
const fetchImpl = args.fetchImpl || fetch;
|
|
6537
|
+
const now = args.now || /* @__PURE__ */ new Date();
|
|
6538
|
+
const windowDays = args.windowDays ?? DEFAULT_WINDOW_DAYS;
|
|
6539
|
+
const pageSize = args.pageSize ?? PAGE_SIZE;
|
|
6540
|
+
const start = new Date(now.getTime() - windowDays * 24 * 60 * 60 * 1e3);
|
|
6541
|
+
const events = [];
|
|
6542
|
+
let page = 1;
|
|
6543
|
+
let total = Number.POSITIVE_INFINITY;
|
|
6544
|
+
while ((page - 1) * pageSize < total) {
|
|
6545
|
+
const body = JSON.stringify({
|
|
6546
|
+
page,
|
|
6547
|
+
pageSize,
|
|
6548
|
+
startDate: String(start.getTime()),
|
|
6549
|
+
endDate: String(now.getTime())
|
|
6550
|
+
});
|
|
6551
|
+
const response = await fetchImpl(CURSOR_DASHBOARD_USAGE_URL, {
|
|
6552
|
+
method: "POST",
|
|
6553
|
+
headers: {
|
|
6554
|
+
"Content-Type": "application/json",
|
|
6555
|
+
Accept: "application/json",
|
|
6556
|
+
Cookie: cookie,
|
|
6557
|
+
Origin: "https://cursor.com",
|
|
6558
|
+
Referer: "https://cursor.com/settings",
|
|
6559
|
+
"User-Agent": "vibetime-cli"
|
|
6560
|
+
},
|
|
6561
|
+
body,
|
|
6562
|
+
signal: AbortSignal.timeout(2e4)
|
|
6563
|
+
});
|
|
6564
|
+
if (!response.ok) {
|
|
6565
|
+
return /* @__PURE__ */ new Map();
|
|
6566
|
+
}
|
|
6567
|
+
const payload = await response.json();
|
|
6568
|
+
if (!isPlainObject(payload)) {
|
|
6569
|
+
return /* @__PURE__ */ new Map();
|
|
6570
|
+
}
|
|
6571
|
+
const pageEvents = Array.isArray(payload.usageEventsDisplay) ? payload.usageEventsDisplay : [];
|
|
6572
|
+
events.push(...pageEvents);
|
|
6573
|
+
const reported = numberField(payload, "totalUsageEventsCount");
|
|
6574
|
+
total = reported ?? pageEvents.length;
|
|
6575
|
+
if (pageEvents.length < pageSize) {
|
|
6576
|
+
break;
|
|
6577
|
+
}
|
|
6578
|
+
page += 1;
|
|
6579
|
+
}
|
|
6580
|
+
return groupCursorUsageEvents(events);
|
|
6581
|
+
}
|
|
6582
|
+
function serializeCursorCloudUsageCache(sessions) {
|
|
6583
|
+
const sorted = [...sessions].sort((a, b) => a.conversationId.localeCompare(b.conversationId));
|
|
6584
|
+
return `${JSON.stringify({ version: CURSOR_CLOUD_USAGE_CACHE_VERSION, sessions: sorted }, null, 2)}
|
|
6585
|
+
`;
|
|
6586
|
+
}
|
|
6587
|
+
function parseCursorCloudUsageCache(raw) {
|
|
6588
|
+
if (!isPlainObject(raw) || raw.version !== CURSOR_CLOUD_USAGE_CACHE_VERSION || !Array.isArray(raw.sessions)) {
|
|
6589
|
+
return [];
|
|
6590
|
+
}
|
|
6591
|
+
const sessions = [];
|
|
6592
|
+
for (const item of raw.sessions) {
|
|
6593
|
+
if (!isPlainObject(item)) {
|
|
6594
|
+
continue;
|
|
6595
|
+
}
|
|
6596
|
+
const conversationId = stringField(item, "conversationId")?.trim();
|
|
6597
|
+
const ts = stringField(item, "ts");
|
|
6598
|
+
const startedAt = stringField(item, "startedAt") || ts;
|
|
6599
|
+
if (!conversationId || !ts || !startedAt) {
|
|
6600
|
+
continue;
|
|
6601
|
+
}
|
|
6602
|
+
sessions.push({
|
|
6603
|
+
conversationId,
|
|
6604
|
+
model: stringField(item, "model"),
|
|
6605
|
+
ts,
|
|
6606
|
+
startedAt,
|
|
6607
|
+
tokensInput: numberField(item, "tokensInput") || 0,
|
|
6608
|
+
tokensOutput: numberField(item, "tokensOutput") || 0,
|
|
6609
|
+
tokensCacheReadInput: numberField(item, "tokensCacheReadInput") || 0,
|
|
6610
|
+
tokensCacheCreationInput: numberField(item, "tokensCacheCreationInput") || 0
|
|
6611
|
+
});
|
|
6612
|
+
}
|
|
6613
|
+
return sessions;
|
|
6614
|
+
}
|
|
6615
|
+
function jwtPayload(token) {
|
|
6616
|
+
const segment = token.split(".")[1];
|
|
6617
|
+
if (!segment) {
|
|
6618
|
+
return void 0;
|
|
6619
|
+
}
|
|
6620
|
+
try {
|
|
6621
|
+
const json = Buffer.from(segment, "base64url").toString("utf8");
|
|
6622
|
+
const parsed = JSON.parse(json);
|
|
6623
|
+
return isPlainObject(parsed) ? parsed : void 0;
|
|
6624
|
+
} catch {
|
|
6625
|
+
return void 0;
|
|
6626
|
+
}
|
|
6627
|
+
}
|
|
6628
|
+
|
|
6629
|
+
// src/adapters/cursor.ts
|
|
6403
6630
|
var SOURCE_ID = "cursor";
|
|
6404
6631
|
var AGENT_NAME = "cursor";
|
|
6405
6632
|
var HOOK_COMMAND = `vibetime hook --agent ${SOURCE_ID}`;
|
|
@@ -6468,6 +6695,297 @@ function cursorStateDbCandidates(home, env) {
|
|
|
6468
6695
|
}
|
|
6469
6696
|
return candidates;
|
|
6470
6697
|
}
|
|
6698
|
+
var cursorCloudUsageByOptions = /* @__PURE__ */ new WeakMap();
|
|
6699
|
+
function isCursorStateDbPath(filePath) {
|
|
6700
|
+
if (!filePath) {
|
|
6701
|
+
return false;
|
|
6702
|
+
}
|
|
6703
|
+
const base = path13.basename(filePath);
|
|
6704
|
+
return base === "state.vscdb" || base.endsWith(".vscdb");
|
|
6705
|
+
}
|
|
6706
|
+
function injectedCloudEvent(conversationId, item) {
|
|
6707
|
+
const tokensInput = numberField(item, "tokensInput") || numberField(item, "inputTokens") || 0;
|
|
6708
|
+
const tokensOutput = numberField(item, "tokensOutput") || numberField(item, "outputTokens") || 0;
|
|
6709
|
+
const tokensCacheReadInput = numberField(item, "tokensCacheReadInput") || numberField(item, "cacheReadTokens") || 0;
|
|
6710
|
+
const tokensCacheCreationInput = numberField(item, "tokensCacheCreationInput") || numberField(item, "cacheWriteTokens") || 0;
|
|
6711
|
+
if (tokensInput <= 0 && tokensOutput <= 0 && tokensCacheReadInput <= 0 && tokensCacheCreationInput <= 0) {
|
|
6712
|
+
return void 0;
|
|
6713
|
+
}
|
|
6714
|
+
const ts = stringField(item, "ts") || (/* @__PURE__ */ new Date(0)).toISOString();
|
|
6715
|
+
return {
|
|
6716
|
+
conversationId,
|
|
6717
|
+
model: stringField(item, "model"),
|
|
6718
|
+
ts,
|
|
6719
|
+
startedAt: stringField(item, "startedAt") || void 0,
|
|
6720
|
+
tokensInput,
|
|
6721
|
+
tokensOutput,
|
|
6722
|
+
tokensCacheReadInput,
|
|
6723
|
+
tokensCacheCreationInput
|
|
6724
|
+
};
|
|
6725
|
+
}
|
|
6726
|
+
function injectedCursorCloudUsage(options) {
|
|
6727
|
+
if (!Object.prototype.hasOwnProperty.call(options, "cursorCloudUsage")) {
|
|
6728
|
+
return void 0;
|
|
6729
|
+
}
|
|
6730
|
+
const raw = options.cursorCloudUsage;
|
|
6731
|
+
const map = /* @__PURE__ */ new Map();
|
|
6732
|
+
if (!isPlainObject(raw)) {
|
|
6733
|
+
return map;
|
|
6734
|
+
}
|
|
6735
|
+
for (const [conversationId, item] of Object.entries(raw)) {
|
|
6736
|
+
if (!conversationId) {
|
|
6737
|
+
continue;
|
|
6738
|
+
}
|
|
6739
|
+
const events = [];
|
|
6740
|
+
if (Array.isArray(item)) {
|
|
6741
|
+
for (const entry of item) {
|
|
6742
|
+
if (isPlainObject(entry)) {
|
|
6743
|
+
const parsed = injectedCloudEvent(conversationId, entry);
|
|
6744
|
+
if (parsed) {
|
|
6745
|
+
events.push(parsed);
|
|
6746
|
+
}
|
|
6747
|
+
}
|
|
6748
|
+
}
|
|
6749
|
+
} else if (isPlainObject(item) && Array.isArray(item.events)) {
|
|
6750
|
+
for (const entry of item.events) {
|
|
6751
|
+
if (isPlainObject(entry)) {
|
|
6752
|
+
const parsed = injectedCloudEvent(conversationId, entry);
|
|
6753
|
+
if (parsed) {
|
|
6754
|
+
events.push(parsed);
|
|
6755
|
+
}
|
|
6756
|
+
}
|
|
6757
|
+
}
|
|
6758
|
+
} else if (isPlainObject(item)) {
|
|
6759
|
+
const parsed = injectedCloudEvent(conversationId, item);
|
|
6760
|
+
if (parsed) {
|
|
6761
|
+
events.push(parsed);
|
|
6762
|
+
}
|
|
6763
|
+
}
|
|
6764
|
+
if (events.length > 0) {
|
|
6765
|
+
map.set(conversationId, events);
|
|
6766
|
+
}
|
|
6767
|
+
}
|
|
6768
|
+
return map;
|
|
6769
|
+
}
|
|
6770
|
+
async function readCursorAccessToken(dbPath) {
|
|
6771
|
+
if (!isCursorStateDbPath(dbPath)) {
|
|
6772
|
+
return void 0;
|
|
6773
|
+
}
|
|
6774
|
+
const info = await stat8(dbPath).catch(() => null);
|
|
6775
|
+
if (!info) {
|
|
6776
|
+
return void 0;
|
|
6777
|
+
}
|
|
6778
|
+
try {
|
|
6779
|
+
const opened = await openCursorDb(dbPath);
|
|
6780
|
+
try {
|
|
6781
|
+
const row = opened.db.prepare(
|
|
6782
|
+
"SELECT value FROM ItemTable WHERE key = 'cursorAuth/accessToken'"
|
|
6783
|
+
).get();
|
|
6784
|
+
const token = decodeKv(row?.value)?.trim();
|
|
6785
|
+
return token || void 0;
|
|
6786
|
+
} finally {
|
|
6787
|
+
opened.db.close();
|
|
6788
|
+
await opened.cleanup();
|
|
6789
|
+
}
|
|
6790
|
+
} catch {
|
|
6791
|
+
return void 0;
|
|
6792
|
+
}
|
|
6793
|
+
}
|
|
6794
|
+
async function loadCursorCloudUsageMap(options, filePath) {
|
|
6795
|
+
const injected = injectedCursorCloudUsage(options);
|
|
6796
|
+
if (injected) {
|
|
6797
|
+
return injected;
|
|
6798
|
+
}
|
|
6799
|
+
if (cursorCloudUsageDisabled()) {
|
|
6800
|
+
return /* @__PURE__ */ new Map();
|
|
6801
|
+
}
|
|
6802
|
+
const cached = cursorCloudUsageByOptions.get(options);
|
|
6803
|
+
if (cached) {
|
|
6804
|
+
return cached;
|
|
6805
|
+
}
|
|
6806
|
+
const pending = (async () => {
|
|
6807
|
+
try {
|
|
6808
|
+
const token = isCursorStateDbPath(filePath) ? await readCursorAccessToken(filePath) : await readCursorAccessTokenFromHome(options);
|
|
6809
|
+
if (!token) {
|
|
6810
|
+
return /* @__PURE__ */ new Map();
|
|
6811
|
+
}
|
|
6812
|
+
const fetchImpl = typeof options.cursorCloudFetch === "function" ? options.cursorCloudFetch : void 0;
|
|
6813
|
+
return await fetchCursorDashboardUsage({ accessToken: token, fetchImpl });
|
|
6814
|
+
} catch {
|
|
6815
|
+
return /* @__PURE__ */ new Map();
|
|
6816
|
+
}
|
|
6817
|
+
})();
|
|
6818
|
+
cursorCloudUsageByOptions.set(options, pending);
|
|
6819
|
+
return pending;
|
|
6820
|
+
}
|
|
6821
|
+
async function readCursorAccessTokenFromHome(options) {
|
|
6822
|
+
const home = stringOption(options.home) || os6.homedir();
|
|
6823
|
+
const env = {
|
|
6824
|
+
CURSOR_HOME: process.env.CURSOR_HOME,
|
|
6825
|
+
CURSOR_USER_DIR: process.env.CURSOR_USER_DIR
|
|
6826
|
+
};
|
|
6827
|
+
for (const candidate of cursorStateDbCandidates(home, env)) {
|
|
6828
|
+
const token = await readCursorAccessToken(candidate);
|
|
6829
|
+
if (token) {
|
|
6830
|
+
return token;
|
|
6831
|
+
}
|
|
6832
|
+
}
|
|
6833
|
+
return void 0;
|
|
6834
|
+
}
|
|
6835
|
+
function cloudUsageToPersisted(row, index = 0) {
|
|
6836
|
+
return {
|
|
6837
|
+
generationId: `${CURSOR_CLOUD_USAGE_GENERATION_ID}:${index}:${row.tokensInput}:${row.tokensOutput}:${row.tokensCacheReadInput}`,
|
|
6838
|
+
ts: row.ts,
|
|
6839
|
+
model: row.model,
|
|
6840
|
+
tokensInput: row.tokensInput || void 0,
|
|
6841
|
+
tokensOutput: row.tokensOutput || void 0,
|
|
6842
|
+
tokensCacheReadInput: row.tokensCacheReadInput || void 0,
|
|
6843
|
+
tokensCacheCreationInput: row.tokensCacheCreationInput || void 0
|
|
6844
|
+
};
|
|
6845
|
+
}
|
|
6846
|
+
async function resolveCursorSessionUsage(options, sessionId, filePath) {
|
|
6847
|
+
const persisted = await readPersistedSessionContextFromOptions(options, sessionId);
|
|
6848
|
+
const cloudEvents = (await loadCursorCloudUsageMap(options, filePath)).get(sessionId) || [];
|
|
6849
|
+
if (persisted?.usage?.length) {
|
|
6850
|
+
return [
|
|
6851
|
+
...persisted.usage,
|
|
6852
|
+
...unmatchedCloudEvents(cloudEvents, persisted.usage).map((event, index) => cloudUsageToPersisted(event, index))
|
|
6853
|
+
];
|
|
6854
|
+
}
|
|
6855
|
+
return cloudEvents.map((event, index) => cloudUsageToPersisted(event, index));
|
|
6856
|
+
}
|
|
6857
|
+
function cursorCloudUsageCachePath(home) {
|
|
6858
|
+
return path13.join(home, ".vibetime", CURSOR_CLOUD_USAGE_FILENAME);
|
|
6859
|
+
}
|
|
6860
|
+
async function listLocalCursorSessionIds(home, env) {
|
|
6861
|
+
const ids = /* @__PURE__ */ new Set();
|
|
6862
|
+
for (const dbPath of cursorStateDbCandidates(home, env)) {
|
|
6863
|
+
const composerIds = await listCursorComposerIds(dbPath);
|
|
6864
|
+
for (const id of composerIds) {
|
|
6865
|
+
ids.add(id);
|
|
6866
|
+
}
|
|
6867
|
+
if (composerIds.size > 0 || await stat8(dbPath).catch(() => null)) {
|
|
6868
|
+
break;
|
|
6869
|
+
}
|
|
6870
|
+
}
|
|
6871
|
+
const projects = cursorProjectsDir(home, env);
|
|
6872
|
+
for (const filePath of await listJsonlFiles(projects)) {
|
|
6873
|
+
if (!isCursorTranscriptPath(filePath) && path13.basename(path13.dirname(filePath)) !== "agent-transcripts") {
|
|
6874
|
+
continue;
|
|
6875
|
+
}
|
|
6876
|
+
const sessionId = path13.basename(filePath, ".jsonl");
|
|
6877
|
+
if (sessionId) {
|
|
6878
|
+
ids.add(sessionId);
|
|
6879
|
+
}
|
|
6880
|
+
}
|
|
6881
|
+
return ids;
|
|
6882
|
+
}
|
|
6883
|
+
async function writeCursorCloudUsageCache(cachePath, sessions) {
|
|
6884
|
+
const next = serializeCursorCloudUsageCache(sessions);
|
|
6885
|
+
const existing = await readFile8(cachePath, "utf8").catch(() => "");
|
|
6886
|
+
if (existing === next) {
|
|
6887
|
+
const info2 = await stat8(cachePath);
|
|
6888
|
+
return info2.mtime.toISOString();
|
|
6889
|
+
}
|
|
6890
|
+
await mkdir4(path13.dirname(cachePath), { recursive: true });
|
|
6891
|
+
await writeFile4(cachePath, next, "utf8");
|
|
6892
|
+
const info = await stat8(cachePath);
|
|
6893
|
+
return info.mtime.toISOString();
|
|
6894
|
+
}
|
|
6895
|
+
async function appendCursorCloudAgentSource(files, home, env, options) {
|
|
6896
|
+
const dbPath = files.find((file) => isCursorStateDbPath(file.path))?.path;
|
|
6897
|
+
const map = await loadCursorCloudUsageMap(options, dbPath);
|
|
6898
|
+
const localIds = await listLocalCursorSessionIds(home, env);
|
|
6899
|
+
const cloudOnly = [];
|
|
6900
|
+
for (const [conversationId, events] of map) {
|
|
6901
|
+
if (localIds.has(conversationId)) {
|
|
6902
|
+
continue;
|
|
6903
|
+
}
|
|
6904
|
+
const summed = sumCursorCloudEvents(conversationId, events);
|
|
6905
|
+
if (summed) {
|
|
6906
|
+
cloudOnly.push(summed);
|
|
6907
|
+
}
|
|
6908
|
+
}
|
|
6909
|
+
if (cloudOnly.length === 0) {
|
|
6910
|
+
return;
|
|
6911
|
+
}
|
|
6912
|
+
const cachePath = cursorCloudUsageCachePath(home);
|
|
6913
|
+
files.push({
|
|
6914
|
+
path: cachePath,
|
|
6915
|
+
modifiedAt: await writeCursorCloudUsageCache(cachePath, cloudOnly)
|
|
6916
|
+
});
|
|
6917
|
+
}
|
|
6918
|
+
function parseCursorCloudAgentSessions(filePath, options, sessions, localIds) {
|
|
6919
|
+
const events = [];
|
|
6920
|
+
const sourcePathHash = `sha256:${createStableHash(filePath)}`;
|
|
6921
|
+
const project = CURSOR_CLOUD_AGENT_PROJECT;
|
|
6922
|
+
const workspaceId = createWorkspaceId({ projectName: project });
|
|
6923
|
+
let lineNumber = 0;
|
|
6924
|
+
for (const row of sessions) {
|
|
6925
|
+
if (localIds.has(row.conversationId) || !row.startedAt) {
|
|
6926
|
+
continue;
|
|
6927
|
+
}
|
|
6928
|
+
const sessionId = row.conversationId;
|
|
6929
|
+
const model = row.model;
|
|
6930
|
+
const endedAt = row.ts || row.startedAt;
|
|
6931
|
+
const push = (partial, topType) => {
|
|
6932
|
+
lineNumber += 1;
|
|
6933
|
+
const event = {
|
|
6934
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6935
|
+
source: SOURCE_ID,
|
|
6936
|
+
agent: AGENT_NAME,
|
|
6937
|
+
workspaceId,
|
|
6938
|
+
project,
|
|
6939
|
+
model,
|
|
6940
|
+
sessionId,
|
|
6941
|
+
...partial
|
|
6942
|
+
};
|
|
6943
|
+
events.push(withBackfillRefs(event, {
|
|
6944
|
+
filePath,
|
|
6945
|
+
sourcePathHash,
|
|
6946
|
+
lineNumber,
|
|
6947
|
+
topType,
|
|
6948
|
+
payloadType: event.type,
|
|
6949
|
+
options
|
|
6950
|
+
}));
|
|
6951
|
+
};
|
|
6952
|
+
push({
|
|
6953
|
+
ts: row.startedAt,
|
|
6954
|
+
type: "session.started",
|
|
6955
|
+
confidence: "partial",
|
|
6956
|
+
refs: stringRefs({ sourceId: `${sessionId}:started` })
|
|
6957
|
+
}, "cloud-agent");
|
|
6958
|
+
emitPersistedCursorUsage(
|
|
6959
|
+
push,
|
|
6960
|
+
[cloudUsageToPersisted(row)],
|
|
6961
|
+
endedAt,
|
|
6962
|
+
void 0,
|
|
6963
|
+
model,
|
|
6964
|
+
sessionId
|
|
6965
|
+
);
|
|
6966
|
+
push({
|
|
6967
|
+
ts: endedAt,
|
|
6968
|
+
type: "session.ended",
|
|
6969
|
+
confidence: "partial",
|
|
6970
|
+
refs: stringRefs({ sourceId: `${sessionId}:ended` })
|
|
6971
|
+
}, "cloud-agent");
|
|
6972
|
+
}
|
|
6973
|
+
return events;
|
|
6974
|
+
}
|
|
6975
|
+
async function parseCursorCloudAgentFile(filePath, options) {
|
|
6976
|
+
const home = stringOption(options.home) || os6.homedir();
|
|
6977
|
+
const env = {
|
|
6978
|
+
CURSOR_HOME: process.env.CURSOR_HOME,
|
|
6979
|
+
CURSOR_USER_DIR: process.env.CURSOR_USER_DIR
|
|
6980
|
+
};
|
|
6981
|
+
const injected = injectedCursorCloudUsage(options);
|
|
6982
|
+
const sessions = injected ? [...injected.entries()].flatMap(([conversationId, events]) => {
|
|
6983
|
+
const summed = sumCursorCloudEvents(conversationId, events);
|
|
6984
|
+
return summed ? [summed] : [];
|
|
6985
|
+
}) : parseCursorCloudUsageCache(await readJsonIfExists(filePath));
|
|
6986
|
+
const localIds = await listLocalCursorSessionIds(home, env);
|
|
6987
|
+
return parseCursorCloudAgentSessions(filePath, options, sessions, localIds);
|
|
6988
|
+
}
|
|
6471
6989
|
function decodeKv(value) {
|
|
6472
6990
|
if (typeof value === "string") {
|
|
6473
6991
|
return value;
|
|
@@ -7133,6 +7651,7 @@ async function parseCursorTranscriptFile(filePath, options) {
|
|
|
7133
7651
|
push,
|
|
7134
7652
|
sessionId,
|
|
7135
7653
|
options,
|
|
7654
|
+
filePath,
|
|
7136
7655
|
fallbackTs: endedAt || lastTs,
|
|
7137
7656
|
lastTurnId,
|
|
7138
7657
|
model,
|
|
@@ -7144,10 +7663,9 @@ async function appendPersistedCursorUsage(args) {
|
|
|
7144
7663
|
if (args.skip) {
|
|
7145
7664
|
return;
|
|
7146
7665
|
}
|
|
7147
|
-
const persisted = await readPersistedSessionContextFromOptions(args.options, args.sessionId);
|
|
7148
7666
|
emitPersistedCursorUsage(
|
|
7149
7667
|
args.push,
|
|
7150
|
-
|
|
7668
|
+
await resolveCursorSessionUsage(args.options, args.sessionId, args.filePath),
|
|
7151
7669
|
args.fallbackTs,
|
|
7152
7670
|
args.lastTurnId,
|
|
7153
7671
|
args.model,
|
|
@@ -7168,15 +7686,16 @@ function emitPersistedCursorUsage(push, usage, fallbackTs, lastTurnId, model, se
|
|
|
7168
7686
|
if (!metrics) {
|
|
7169
7687
|
continue;
|
|
7170
7688
|
}
|
|
7689
|
+
const fromCloud = item.generationId.startsWith(CURSOR_CLOUD_USAGE_GENERATION_ID);
|
|
7171
7690
|
push({
|
|
7172
7691
|
ts: timestampFrom(item.ts) || fallbackTs,
|
|
7173
7692
|
type: "model.usage",
|
|
7174
7693
|
turnId: lastTurnId,
|
|
7175
7694
|
model: item.model || model,
|
|
7176
|
-
confidence: "exact",
|
|
7695
|
+
confidence: fromCloud ? "partial" : "exact",
|
|
7177
7696
|
metrics,
|
|
7178
7697
|
refs: stringRefs({ sourceId: `${sessionId}:${item.generationId}:usage` })
|
|
7179
|
-
}, "hook-usage");
|
|
7698
|
+
}, fromCloud ? "cloud-usage" : "hook-usage");
|
|
7180
7699
|
}
|
|
7181
7700
|
}
|
|
7182
7701
|
async function listCursorComposerIds(dbPath) {
|
|
@@ -7235,6 +7754,9 @@ async function collectCursorTranscriptFiles(root, home, skipSessionIds) {
|
|
|
7235
7754
|
}
|
|
7236
7755
|
async function parseCursorSessionFile(filePath, options) {
|
|
7237
7756
|
const base = path13.basename(filePath);
|
|
7757
|
+
if (base === CURSOR_CLOUD_USAGE_FILENAME) {
|
|
7758
|
+
return parseCursorCloudAgentFile(filePath, options);
|
|
7759
|
+
}
|
|
7238
7760
|
if (base.endsWith(".jsonl")) {
|
|
7239
7761
|
return parseCursorTranscriptFile(filePath, options);
|
|
7240
7762
|
}
|
|
@@ -7250,8 +7772,8 @@ async function parseCursorSessionFile(filePath, options) {
|
|
|
7250
7772
|
try {
|
|
7251
7773
|
const composers = listComposers(opened.db);
|
|
7252
7774
|
for (const composer of composers) {
|
|
7253
|
-
const
|
|
7254
|
-
events.push(...parseComposer(opened.db, composer, filePath, sourcePathHash, options,
|
|
7775
|
+
const usage = await resolveCursorSessionUsage(options, composer.composerId, filePath);
|
|
7776
|
+
events.push(...parseComposer(opened.db, composer, filePath, sourcePathHash, options, usage));
|
|
7255
7777
|
}
|
|
7256
7778
|
} finally {
|
|
7257
7779
|
opened.db.close();
|
|
@@ -7472,7 +7994,7 @@ function parseComposer(db, composer, filePath, sourcePathHash, options, persiste
|
|
|
7472
7994
|
emitPersistedCursorUsage(push, persistedUsage, endedAt || startedAt, currentTurnId, model, sessionId, events.some((event) => event.type === "model.usage"));
|
|
7473
7995
|
return events;
|
|
7474
7996
|
}
|
|
7475
|
-
async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env) {
|
|
7997
|
+
async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env, options) {
|
|
7476
7998
|
if (sourceRoot) {
|
|
7477
7999
|
const info = await stat8(sourceRoot).catch(() => null);
|
|
7478
8000
|
if (!info) {
|
|
@@ -7539,6 +8061,9 @@ async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env) {
|
|
|
7539
8061
|
break;
|
|
7540
8062
|
}
|
|
7541
8063
|
files.push(...await collectCursorTranscriptFiles(cursorProjectsDir(home, env), home, skipIds));
|
|
8064
|
+
if (options) {
|
|
8065
|
+
await appendCursorCloudAgentSource(files, home, env, options);
|
|
8066
|
+
}
|
|
7542
8067
|
return files;
|
|
7543
8068
|
}
|
|
7544
8069
|
function cursorHookConfig() {
|
|
@@ -13984,7 +14509,7 @@ async function uninstallEntry(entry, options) {
|
|
|
13984
14509
|
await uninstallGeneratedFile(entry.path, options);
|
|
13985
14510
|
}
|
|
13986
14511
|
async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
|
|
13987
|
-
const { mkdir:
|
|
14512
|
+
const { mkdir: mkdir7, writeFile: writeFile6 } = await import("node:fs/promises");
|
|
13988
14513
|
const pathMod = await import("node:path");
|
|
13989
14514
|
if (dryRun) {
|
|
13990
14515
|
onWrite(`Would merge ${filePath}`);
|
|
@@ -14005,12 +14530,12 @@ async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
|
|
|
14005
14530
|
onWrite(`Already installed ${filePath}`);
|
|
14006
14531
|
return;
|
|
14007
14532
|
}
|
|
14008
|
-
await
|
|
14009
|
-
await
|
|
14533
|
+
await mkdir7(pathMod.dirname(filePath), { recursive: true });
|
|
14534
|
+
await writeFile6(filePath, nextText, "utf8");
|
|
14010
14535
|
onWrite(`Installed ${filePath}`);
|
|
14011
14536
|
}
|
|
14012
14537
|
async function mergeCursorHooksFile(filePath, content, { dryRun, force, onWrite }) {
|
|
14013
|
-
const { mkdir:
|
|
14538
|
+
const { mkdir: mkdir7, writeFile: writeFile6 } = await import("node:fs/promises");
|
|
14014
14539
|
const pathMod = await import("node:path");
|
|
14015
14540
|
if (dryRun) {
|
|
14016
14541
|
onWrite(`Would merge ${filePath}`);
|
|
@@ -14030,8 +14555,8 @@ async function mergeCursorHooksFile(filePath, content, { dryRun, force, onWrite
|
|
|
14030
14555
|
onWrite(`Already installed ${filePath}`);
|
|
14031
14556
|
return;
|
|
14032
14557
|
}
|
|
14033
|
-
await
|
|
14034
|
-
await
|
|
14558
|
+
await mkdir7(pathMod.dirname(filePath), { recursive: true });
|
|
14559
|
+
await writeFile6(filePath, nextText, "utf8");
|
|
14035
14560
|
onWrite(`Installed ${filePath}`);
|
|
14036
14561
|
}
|
|
14037
14562
|
async function uninstallCursorHooksFile(filePath, content, { dryRun, onWrite }) {
|
|
@@ -14065,8 +14590,8 @@ async function uninstallCursorHooksFile(filePath, content, { dryRun, onWrite })
|
|
|
14065
14590
|
onWrite(`Would uninstall ${filePath}`);
|
|
14066
14591
|
return;
|
|
14067
14592
|
}
|
|
14068
|
-
const { writeFile:
|
|
14069
|
-
await
|
|
14593
|
+
const { writeFile: writeFile6 } = await import("node:fs/promises");
|
|
14594
|
+
await writeFile6(filePath, `${JSON.stringify(next, null, 2)}
|
|
14070
14595
|
`, "utf8");
|
|
14071
14596
|
onWrite(`Uninstalled ${filePath}`);
|
|
14072
14597
|
}
|
|
@@ -14141,7 +14666,7 @@ function hookCommandFromGroup(group) {
|
|
|
14141
14666
|
return isPlainObject(hook) && typeof hook.command === "string" ? hook.command : void 0;
|
|
14142
14667
|
}
|
|
14143
14668
|
async function mergeHooksToml(filePath, content, { dryRun, force, onWrite }) {
|
|
14144
|
-
const { mkdir:
|
|
14669
|
+
const { mkdir: mkdir7, writeFile: writeFile6 } = await import("node:fs/promises");
|
|
14145
14670
|
const pathMod = await import("node:path");
|
|
14146
14671
|
if (dryRun) {
|
|
14147
14672
|
onWrite(`Would merge ${filePath}`);
|
|
@@ -14169,8 +14694,8 @@ async function mergeHooksToml(filePath, content, { dryRun, force, onWrite }) {
|
|
|
14169
14694
|
return;
|
|
14170
14695
|
}
|
|
14171
14696
|
}
|
|
14172
|
-
await
|
|
14173
|
-
await
|
|
14697
|
+
await mkdir7(pathMod.dirname(filePath), { recursive: true });
|
|
14698
|
+
await writeFile6(filePath, nextText, "utf8");
|
|
14174
14699
|
onWrite(`Installed ${filePath}`);
|
|
14175
14700
|
}
|
|
14176
14701
|
async function uninstallHooksToml(filePath, content, { dryRun, onWrite }) {
|
|
@@ -14195,8 +14720,8 @@ async function uninstallHooksToml(filePath, content, { dryRun, onWrite }) {
|
|
|
14195
14720
|
onWrite(`Would uninstall ${filePath}`);
|
|
14196
14721
|
return;
|
|
14197
14722
|
}
|
|
14198
|
-
const { writeFile:
|
|
14199
|
-
await
|
|
14723
|
+
const { writeFile: writeFile6 } = await import("node:fs/promises");
|
|
14724
|
+
await writeFile6(filePath, nextText, "utf8");
|
|
14200
14725
|
onWrite(`Uninstalled ${filePath}`);
|
|
14201
14726
|
}
|
|
14202
14727
|
function collectHookCommandsFromJsonContent(content) {
|
|
@@ -14329,8 +14854,8 @@ async function uninstallHooksJson(filePath, content, { dryRun, onWrite }) {
|
|
|
14329
14854
|
onWrite(`Would uninstall ${filePath}`);
|
|
14330
14855
|
return;
|
|
14331
14856
|
}
|
|
14332
|
-
const { writeFile:
|
|
14333
|
-
await
|
|
14857
|
+
const { writeFile: writeFile6 } = await import("node:fs/promises");
|
|
14858
|
+
await writeFile6(filePath, `${JSON.stringify(next, null, 2)}
|
|
14334
14859
|
`, "utf8");
|
|
14335
14860
|
onWrite(`Uninstalled ${filePath}`);
|
|
14336
14861
|
}
|
|
@@ -14412,7 +14937,7 @@ function defaultMachineName() {
|
|
|
14412
14937
|
init_fs();
|
|
14413
14938
|
|
|
14414
14939
|
// src/lib/logger.ts
|
|
14415
|
-
import { appendFile, mkdir as
|
|
14940
|
+
import { appendFile, mkdir as mkdir5, rename, stat as stat15 } from "node:fs/promises";
|
|
14416
14941
|
import { homedir as homedir2 } from "node:os";
|
|
14417
14942
|
import path25 from "node:path";
|
|
14418
14943
|
var MAX_BYTES = 1 * 1024 * 1024;
|
|
@@ -14441,7 +14966,7 @@ async function rotateIfNeeded(file) {
|
|
|
14441
14966
|
async function writeLog(entry, home = homedir2(), fileName = "cli.log") {
|
|
14442
14967
|
try {
|
|
14443
14968
|
const dir = logDir(home);
|
|
14444
|
-
await
|
|
14969
|
+
await mkdir5(dir, { recursive: true });
|
|
14445
14970
|
const file = logPath(home, fileName);
|
|
14446
14971
|
await rotateIfNeeded(file);
|
|
14447
14972
|
const record = {
|
|
@@ -14666,7 +15191,7 @@ async function deleteMachine(remote, id) {
|
|
|
14666
15191
|
}
|
|
14667
15192
|
|
|
14668
15193
|
// src/lib/types.ts
|
|
14669
|
-
var BACKFILL_STATE_SCHEMA_VERSION =
|
|
15194
|
+
var BACKFILL_STATE_SCHEMA_VERSION = 9;
|
|
14670
15195
|
|
|
14671
15196
|
// src/cli.ts
|
|
14672
15197
|
function createRegistry() {
|
|
@@ -15245,7 +15770,7 @@ async function listBackfillSourceFiles(source, options, ctx) {
|
|
|
15245
15770
|
return zedBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
15246
15771
|
}
|
|
15247
15772
|
if (source.id === "cursor") {
|
|
15248
|
-
return cursorBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
15773
|
+
return cursorBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env, options);
|
|
15249
15774
|
}
|
|
15250
15775
|
if (source.id === "pi") {
|
|
15251
15776
|
return piBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
@@ -15654,8 +16179,8 @@ async function readBackfillIncrementalStateFile(home, ctx) {
|
|
|
15654
16179
|
}
|
|
15655
16180
|
async function writeBackfillIncrementalStateFile(home, file) {
|
|
15656
16181
|
const statePath = backfillIncrementalStatePath(home);
|
|
15657
|
-
await
|
|
15658
|
-
await
|
|
16182
|
+
await mkdir6(path26.dirname(statePath), { recursive: true });
|
|
16183
|
+
await writeFile5(statePath, `${JSON.stringify(file, null, 2)}
|
|
15659
16184
|
`, "utf8");
|
|
15660
16185
|
}
|
|
15661
16186
|
async function readBackfillIncrementalState(home, remoteKey, ctx) {
|
|
@@ -15703,8 +16228,8 @@ async function readSyncLocalTriggerState(statePath) {
|
|
|
15703
16228
|
return nextState;
|
|
15704
16229
|
}
|
|
15705
16230
|
async function writeSyncLocalTriggerState(statePath, state) {
|
|
15706
|
-
await
|
|
15707
|
-
await
|
|
16231
|
+
await mkdir6(path26.dirname(statePath), { recursive: true });
|
|
16232
|
+
await writeFile5(statePath, `${JSON.stringify(state, null, 2)}
|
|
15708
16233
|
`, "utf8");
|
|
15709
16234
|
}
|
|
15710
16235
|
async function readSyncLocalLock(lockPath) {
|
|
@@ -15718,12 +16243,12 @@ async function readSyncLocalLock(lockPath) {
|
|
|
15718
16243
|
return { pid: lock.pid, startedAt: lock.startedAt };
|
|
15719
16244
|
}
|
|
15720
16245
|
async function writeSyncLocalLock(lockPath, lock) {
|
|
15721
|
-
await
|
|
15722
|
-
await
|
|
16246
|
+
await mkdir6(path26.dirname(lockPath), { recursive: true });
|
|
16247
|
+
await writeFile5(lockPath, `${JSON.stringify(lock, null, 2)}
|
|
15723
16248
|
`, "utf8");
|
|
15724
16249
|
}
|
|
15725
16250
|
async function acquireSyncLocalLock(lockPath, lock) {
|
|
15726
|
-
await
|
|
16251
|
+
await mkdir6(path26.dirname(lockPath), { recursive: true });
|
|
15727
16252
|
try {
|
|
15728
16253
|
const handle = await open(lockPath, "wx");
|
|
15729
16254
|
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.60",
|
|
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": {
|