@yhong91/vibetime 0.1.63 → 0.1.65
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 +320 -187
- package/package.json +1 -1
package/bin/vibetime.mjs
CHANGED
|
@@ -884,8 +884,8 @@ 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 mkdir6, open, rm, stat as
|
|
888
|
-
import
|
|
887
|
+
import { mkdir as mkdir6, open, rm, stat as stat17, writeFile as writeFile5 } from "node:fs/promises";
|
|
888
|
+
import os14 from "node:os";
|
|
889
889
|
import path26 from "node:path";
|
|
890
890
|
import { fileURLToPath } from "node:url";
|
|
891
891
|
|
|
@@ -924,7 +924,7 @@ var TELEMETRY_EVENT_TYPES = [
|
|
|
924
924
|
"agent.operation"
|
|
925
925
|
];
|
|
926
926
|
var FILE_ACTIVITY_OPERATIONS = ["read", "search", "create", "write", "edit", "delete"];
|
|
927
|
-
var BACKFILL_SOURCE_IDS = ["codex", "claude-code", "claude-cowork", "copilot", "opencode", "pi", "agy", "codebuddy", "qoder", "qoder-cn", "workbuddy", "zcode", "grok-build", "zed", "kimi-code", "cursor"];
|
|
927
|
+
var BACKFILL_SOURCE_IDS = ["codex", "claude-code", "claude-cowork", "copilot", "opencode", "pi", "agy", "codebuddy", "qoder", "qoder-cn", "workbuddy", "zcode", "grok-build", "zed", "kimi-code", "cursor", "grok-bot"];
|
|
928
928
|
function createWorkspaceId(input) {
|
|
929
929
|
const basis = input.repoUrl || input.repoRoot || input.projectName || "unknown";
|
|
930
930
|
return `workspace_${fnv1a(basis)}`;
|
|
@@ -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.65" : "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;
|
|
@@ -4936,8 +4936,8 @@ async function codebuddyBackfillFiles(sourceRoot, home, env) {
|
|
|
4936
4936
|
}
|
|
4937
4937
|
const filePath = path9.join(traceDir, entry);
|
|
4938
4938
|
try {
|
|
4939
|
-
const
|
|
4940
|
-
files.push({ path: filePath, modifiedAt:
|
|
4939
|
+
const stat18 = await import("node:fs/promises").then((fs) => fs.stat(filePath));
|
|
4940
|
+
files.push({ path: filePath, modifiedAt: stat18.mtime.toISOString(), groupId: pidDir.name });
|
|
4941
4941
|
} catch {
|
|
4942
4942
|
}
|
|
4943
4943
|
}
|
|
@@ -6331,13 +6331,21 @@ var CURSOR_CLOUD_USAGE_GENERATION_ID = "cursor-cloud";
|
|
|
6331
6331
|
var CURSOR_CLOUD_USAGE_FILENAME = "cursor-cloud-usage.json";
|
|
6332
6332
|
var CURSOR_CLOUD_AGENT_PROJECT = "Cloud Agent";
|
|
6333
6333
|
var CURSOR_DASHBOARD_USAGE_URL = "https://cursor.com/api/dashboard/get-filtered-usage-events";
|
|
6334
|
-
var CURSOR_CLOUD_USAGE_CACHE_VERSION =
|
|
6334
|
+
var CURSOR_CLOUD_USAGE_CACHE_VERSION = 2;
|
|
6335
6335
|
var DEFAULT_WINDOW_DAYS = 90;
|
|
6336
6336
|
var PAGE_SIZE = 1e3;
|
|
6337
|
+
var DEFAULT_CURSOR_CLOUD_FETCH_INTERVAL_SECONDS = 3600;
|
|
6337
6338
|
function cursorCloudUsageDisabled(env = process.env) {
|
|
6338
6339
|
const value = env.VIBETIME_CURSOR_CLOUD_USAGE;
|
|
6339
6340
|
return value === "0" || value === "false";
|
|
6340
6341
|
}
|
|
6342
|
+
function cursorCloudFetchIntervalSeconds(env = process.env) {
|
|
6343
|
+
const parsed = Number.parseInt(env.VIBETIME_CURSOR_CLOUD_FETCH_INTERVAL_SECONDS || "", 10);
|
|
6344
|
+
if (Number.isNaN(parsed) || parsed < 0) {
|
|
6345
|
+
return DEFAULT_CURSOR_CLOUD_FETCH_INTERVAL_SECONDS;
|
|
6346
|
+
}
|
|
6347
|
+
return parsed;
|
|
6348
|
+
}
|
|
6341
6349
|
function cursorSessionCookie(accessToken) {
|
|
6342
6350
|
const token = accessToken.trim();
|
|
6343
6351
|
if (!token) {
|
|
@@ -6396,55 +6404,25 @@ function groupCursorUsageEvents(events) {
|
|
|
6396
6404
|
}
|
|
6397
6405
|
return byConversation;
|
|
6398
6406
|
}
|
|
6399
|
-
function
|
|
6407
|
+
function cloudAgentSessionFromEvents(conversationId, events) {
|
|
6400
6408
|
if (events.length === 0) {
|
|
6401
6409
|
return void 0;
|
|
6402
6410
|
}
|
|
6403
|
-
const
|
|
6404
|
-
|
|
6405
|
-
|
|
6411
|
+
const sorted = [...events].sort((a, b) => {
|
|
6412
|
+
const byTs = Date.parse(a.ts) - Date.parse(b.ts);
|
|
6413
|
+
if (byTs !== 0) {
|
|
6414
|
+
return byTs;
|
|
6415
|
+
}
|
|
6416
|
+
return a.tokensInput - b.tokensInput || a.tokensOutput - b.tokensOutput || a.tokensCacheReadInput - b.tokensCacheReadInput || (a.model || "").localeCompare(b.model || "");
|
|
6417
|
+
});
|
|
6418
|
+
const first = sorted[0];
|
|
6419
|
+
const last = sorted[sorted.length - 1];
|
|
6420
|
+
return {
|
|
6406
6421
|
conversationId,
|
|
6407
|
-
|
|
6408
|
-
ts:
|
|
6409
|
-
|
|
6410
|
-
tokensInput: 0,
|
|
6411
|
-
tokensOutput: 0,
|
|
6412
|
-
tokensCacheReadInput: 0,
|
|
6413
|
-
tokensCacheCreationInput: 0
|
|
6422
|
+
startedAt: first.startedAt || first.ts,
|
|
6423
|
+
ts: last.ts,
|
|
6424
|
+
events: sorted.map((event) => ({ ...event, conversationId }))
|
|
6414
6425
|
};
|
|
6415
|
-
for (const event of events) {
|
|
6416
|
-
summed.tokensInput += event.tokensInput;
|
|
6417
|
-
summed.tokensOutput += event.tokensOutput;
|
|
6418
|
-
summed.tokensCacheReadInput += event.tokensCacheReadInput;
|
|
6419
|
-
summed.tokensCacheCreationInput += event.tokensCacheCreationInput;
|
|
6420
|
-
const eventStart = event.startedAt || event.ts;
|
|
6421
|
-
if (Date.parse(eventStart) < Date.parse(summed.startedAt)) {
|
|
6422
|
-
summed.startedAt = eventStart;
|
|
6423
|
-
}
|
|
6424
|
-
if (Date.parse(event.ts) >= Date.parse(summed.ts)) {
|
|
6425
|
-
summed.ts = event.ts;
|
|
6426
|
-
if (event.model) {
|
|
6427
|
-
summed.model = event.model;
|
|
6428
|
-
}
|
|
6429
|
-
}
|
|
6430
|
-
}
|
|
6431
|
-
return summed;
|
|
6432
|
-
}
|
|
6433
|
-
function hookUncachedInputTokens(turn) {
|
|
6434
|
-
return Math.max(0, (turn.tokensInput || 0) - (turn.tokensCacheReadInput || 0));
|
|
6435
|
-
}
|
|
6436
|
-
function cloudEventMatchesHookTurn(event, turn) {
|
|
6437
|
-
return event.tokensInput === hookUncachedInputTokens(turn) && event.tokensOutput === (turn.tokensOutput || 0) && event.tokensCacheReadInput === (turn.tokensCacheReadInput || 0);
|
|
6438
|
-
}
|
|
6439
|
-
function unmatchedCloudEvents(events, turns) {
|
|
6440
|
-
const used = /* @__PURE__ */ new Set();
|
|
6441
|
-
for (const turn of turns) {
|
|
6442
|
-
const index = events.findIndex((event, i) => !used.has(i) && cloudEventMatchesHookTurn(event, turn));
|
|
6443
|
-
if (index >= 0) {
|
|
6444
|
-
used.add(index);
|
|
6445
|
-
}
|
|
6446
|
-
}
|
|
6447
|
-
return events.filter((_, i) => !used.has(i));
|
|
6448
6426
|
}
|
|
6449
6427
|
async function fetchCursorDashboardUsage(args) {
|
|
6450
6428
|
const cookie = cursorSessionCookie(args.accessToken);
|
|
@@ -6499,11 +6477,46 @@ async function fetchCursorDashboardUsage(args) {
|
|
|
6499
6477
|
}
|
|
6500
6478
|
function serializeCursorCloudUsageCache(sessions) {
|
|
6501
6479
|
const sorted = [...sessions].sort((a, b) => a.conversationId.localeCompare(b.conversationId));
|
|
6502
|
-
|
|
6480
|
+
const payload = {
|
|
6481
|
+
version: CURSOR_CLOUD_USAGE_CACHE_VERSION,
|
|
6482
|
+
sessions: sorted.map((session) => ({
|
|
6483
|
+
conversationId: session.conversationId,
|
|
6484
|
+
startedAt: session.startedAt,
|
|
6485
|
+
ts: session.ts,
|
|
6486
|
+
events: session.events.map((event) => ({
|
|
6487
|
+
model: event.model,
|
|
6488
|
+
ts: event.ts,
|
|
6489
|
+
tokensInput: event.tokensInput,
|
|
6490
|
+
tokensOutput: event.tokensOutput,
|
|
6491
|
+
tokensCacheReadInput: event.tokensCacheReadInput,
|
|
6492
|
+
tokensCacheCreationInput: event.tokensCacheCreationInput
|
|
6493
|
+
}))
|
|
6494
|
+
}))
|
|
6495
|
+
};
|
|
6496
|
+
return `${JSON.stringify(payload, null, 2)}
|
|
6503
6497
|
`;
|
|
6504
6498
|
}
|
|
6499
|
+
function cacheEventFromObject(conversationId, item) {
|
|
6500
|
+
const ts = stringField(item, "ts");
|
|
6501
|
+
if (!ts) {
|
|
6502
|
+
return void 0;
|
|
6503
|
+
}
|
|
6504
|
+
return {
|
|
6505
|
+
conversationId,
|
|
6506
|
+
model: stringField(item, "model"),
|
|
6507
|
+
ts,
|
|
6508
|
+
tokensInput: numberField(item, "tokensInput") || 0,
|
|
6509
|
+
tokensOutput: numberField(item, "tokensOutput") || 0,
|
|
6510
|
+
tokensCacheReadInput: numberField(item, "tokensCacheReadInput") || 0,
|
|
6511
|
+
tokensCacheCreationInput: numberField(item, "tokensCacheCreationInput") || 0
|
|
6512
|
+
};
|
|
6513
|
+
}
|
|
6505
6514
|
function parseCursorCloudUsageCache(raw) {
|
|
6506
|
-
if (!isPlainObject(raw) ||
|
|
6515
|
+
if (!isPlainObject(raw) || !Array.isArray(raw.sessions)) {
|
|
6516
|
+
return [];
|
|
6517
|
+
}
|
|
6518
|
+
const version = numberField(raw, "version");
|
|
6519
|
+
if (version !== 1 && version !== CURSOR_CLOUD_USAGE_CACHE_VERSION) {
|
|
6507
6520
|
return [];
|
|
6508
6521
|
}
|
|
6509
6522
|
const sessions = [];
|
|
@@ -6517,16 +6530,29 @@ function parseCursorCloudUsageCache(raw) {
|
|
|
6517
6530
|
if (!conversationId || !ts || !startedAt) {
|
|
6518
6531
|
continue;
|
|
6519
6532
|
}
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6533
|
+
const events = [];
|
|
6534
|
+
if (Array.isArray(item.events)) {
|
|
6535
|
+
for (const entry of item.events) {
|
|
6536
|
+
if (!isPlainObject(entry)) {
|
|
6537
|
+
continue;
|
|
6538
|
+
}
|
|
6539
|
+
const parsed = cacheEventFromObject(conversationId, entry);
|
|
6540
|
+
if (parsed) {
|
|
6541
|
+
events.push(parsed);
|
|
6542
|
+
}
|
|
6543
|
+
}
|
|
6544
|
+
} else {
|
|
6545
|
+
const parsed = cacheEventFromObject(conversationId, item);
|
|
6546
|
+
if (parsed) {
|
|
6547
|
+
events.push(parsed);
|
|
6548
|
+
}
|
|
6549
|
+
}
|
|
6550
|
+
const session = cloudAgentSessionFromEvents(conversationId, events);
|
|
6551
|
+
if (session) {
|
|
6552
|
+
session.startedAt = startedAt;
|
|
6553
|
+
session.ts = ts;
|
|
6554
|
+
sessions.push(session);
|
|
6555
|
+
}
|
|
6530
6556
|
}
|
|
6531
6557
|
return sessions;
|
|
6532
6558
|
}
|
|
@@ -6721,6 +6747,14 @@ async function loadCursorCloudUsageMap(options, filePath) {
|
|
|
6721
6747
|
if (cached) {
|
|
6722
6748
|
return cached;
|
|
6723
6749
|
}
|
|
6750
|
+
const home = stringOption(options.home) || os6.homedir();
|
|
6751
|
+
const intervalSeconds = cursorCloudFetchIntervalSeconds();
|
|
6752
|
+
if (intervalSeconds > 0 && !options.force) {
|
|
6753
|
+
const lastFetchedMs = await readLastCloudFetchAt(home);
|
|
6754
|
+
if (lastFetchedMs !== void 0 && Date.now() - lastFetchedMs < intervalSeconds * 1e3) {
|
|
6755
|
+
return /* @__PURE__ */ new Map();
|
|
6756
|
+
}
|
|
6757
|
+
}
|
|
6724
6758
|
const pending = (async () => {
|
|
6725
6759
|
try {
|
|
6726
6760
|
const token = isCursorStateDbPath(filePath) ? await readCursorAccessToken(filePath) : await readCursorAccessTokenFromHome(options);
|
|
@@ -6728,7 +6762,10 @@ async function loadCursorCloudUsageMap(options, filePath) {
|
|
|
6728
6762
|
return /* @__PURE__ */ new Map();
|
|
6729
6763
|
}
|
|
6730
6764
|
const fetchImpl = typeof options.cursorCloudFetch === "function" ? options.cursorCloudFetch : void 0;
|
|
6731
|
-
|
|
6765
|
+
const map = await fetchCursorDashboardUsage({ accessToken: token, fetchImpl });
|
|
6766
|
+
await writeLastCloudFetchAt(home, (/* @__PURE__ */ new Date()).toISOString()).catch(() => {
|
|
6767
|
+
});
|
|
6768
|
+
return map;
|
|
6732
6769
|
} catch {
|
|
6733
6770
|
return /* @__PURE__ */ new Map();
|
|
6734
6771
|
}
|
|
@@ -6753,7 +6790,7 @@ async function readCursorAccessTokenFromHome(options) {
|
|
|
6753
6790
|
function cloudUsageToPersisted(row, index = 0) {
|
|
6754
6791
|
const tokensInput = (row.tokensInput || 0) + (row.tokensCacheReadInput || 0) + (row.tokensCacheCreationInput || 0);
|
|
6755
6792
|
return {
|
|
6756
|
-
generationId: `${CURSOR_CLOUD_USAGE_GENERATION_ID}:${index}:${row.tokensInput}:${row.tokensOutput}:${row.tokensCacheReadInput}`,
|
|
6793
|
+
generationId: `${CURSOR_CLOUD_USAGE_GENERATION_ID}:${index}:${row.ts}:${row.tokensInput}:${row.tokensOutput}:${row.tokensCacheReadInput}`,
|
|
6757
6794
|
ts: row.ts,
|
|
6758
6795
|
model: row.model,
|
|
6759
6796
|
tokensInput: tokensInput || void 0,
|
|
@@ -6762,20 +6799,32 @@ function cloudUsageToPersisted(row, index = 0) {
|
|
|
6762
6799
|
tokensCacheCreationInput: row.tokensCacheCreationInput || void 0
|
|
6763
6800
|
};
|
|
6764
6801
|
}
|
|
6765
|
-
async function resolveCursorSessionUsage(options, sessionId
|
|
6802
|
+
async function resolveCursorSessionUsage(options, sessionId) {
|
|
6766
6803
|
const persisted = await readPersistedSessionContextFromOptions(options, sessionId);
|
|
6767
|
-
|
|
6768
|
-
if (persisted?.usage?.length) {
|
|
6769
|
-
return [
|
|
6770
|
-
...persisted.usage,
|
|
6771
|
-
...unmatchedCloudEvents(cloudEvents, persisted.usage).map((event, index) => cloudUsageToPersisted(event, index))
|
|
6772
|
-
];
|
|
6773
|
-
}
|
|
6774
|
-
return cloudEvents.map((event, index) => cloudUsageToPersisted(event, index));
|
|
6804
|
+
return persisted?.usage ?? [];
|
|
6775
6805
|
}
|
|
6776
6806
|
function cursorCloudUsageCachePath(home) {
|
|
6777
6807
|
return path13.join(home, ".vibetime", CURSOR_CLOUD_USAGE_FILENAME);
|
|
6778
6808
|
}
|
|
6809
|
+
function cursorCloudFetchStatePath(home) {
|
|
6810
|
+
return path13.join(home, ".vibetime", "cursor-cloud-usage-fetch.json");
|
|
6811
|
+
}
|
|
6812
|
+
async function readLastCloudFetchAt(home) {
|
|
6813
|
+
try {
|
|
6814
|
+
const raw = JSON.parse(await readFile8(cursorCloudFetchStatePath(home), "utf8"));
|
|
6815
|
+
const ts = isPlainObject(raw) ? stringField(raw, "fetchedAt") : void 0;
|
|
6816
|
+
const ms = ts ? Date.parse(ts) : Number.NaN;
|
|
6817
|
+
return Number.isNaN(ms) ? void 0 : ms;
|
|
6818
|
+
} catch {
|
|
6819
|
+
return void 0;
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6822
|
+
async function writeLastCloudFetchAt(home, fetchedAt) {
|
|
6823
|
+
const statePath = cursorCloudFetchStatePath(home);
|
|
6824
|
+
await mkdir4(path13.dirname(statePath), { recursive: true });
|
|
6825
|
+
await writeFile4(statePath, `${JSON.stringify({ fetchedAt }, null, 2)}
|
|
6826
|
+
`, "utf8");
|
|
6827
|
+
}
|
|
6779
6828
|
async function listLocalCursorSessionIds(home, env) {
|
|
6780
6829
|
const ids = /* @__PURE__ */ new Set();
|
|
6781
6830
|
for (const dbPath of cursorStateDbCandidates(home, env)) {
|
|
@@ -6811,30 +6860,7 @@ async function writeCursorCloudUsageCache(cachePath, sessions) {
|
|
|
6811
6860
|
const info = await stat8(cachePath);
|
|
6812
6861
|
return info.mtime.toISOString();
|
|
6813
6862
|
}
|
|
6814
|
-
|
|
6815
|
-
const dbPath = files.find((file) => isCursorStateDbPath(file.path))?.path;
|
|
6816
|
-
const map = await loadCursorCloudUsageMap(options, dbPath);
|
|
6817
|
-
const localIds = await listLocalCursorSessionIds(home, env);
|
|
6818
|
-
const cloudOnly = [];
|
|
6819
|
-
for (const [conversationId, events] of map) {
|
|
6820
|
-
if (localIds.has(conversationId)) {
|
|
6821
|
-
continue;
|
|
6822
|
-
}
|
|
6823
|
-
const summed = sumCursorCloudEvents(conversationId, events);
|
|
6824
|
-
if (summed) {
|
|
6825
|
-
cloudOnly.push(summed);
|
|
6826
|
-
}
|
|
6827
|
-
}
|
|
6828
|
-
if (cloudOnly.length === 0) {
|
|
6829
|
-
return;
|
|
6830
|
-
}
|
|
6831
|
-
const cachePath = cursorCloudUsageCachePath(home);
|
|
6832
|
-
files.push({
|
|
6833
|
-
path: cachePath,
|
|
6834
|
-
modifiedAt: await writeCursorCloudUsageCache(cachePath, cloudOnly)
|
|
6835
|
-
});
|
|
6836
|
-
}
|
|
6837
|
-
function parseCursorCloudAgentSessions(filePath, options, sessions, localIds) {
|
|
6863
|
+
function parseCursorCloudAgentSessions(filePath, options, sessions, localIds, identity) {
|
|
6838
6864
|
const events = [];
|
|
6839
6865
|
const sourcePathHash = `sha256:${createStableHash(filePath)}`;
|
|
6840
6866
|
const project = CURSOR_CLOUD_AGENT_PROJECT;
|
|
@@ -6845,17 +6871,17 @@ function parseCursorCloudAgentSessions(filePath, options, sessions, localIds) {
|
|
|
6845
6871
|
continue;
|
|
6846
6872
|
}
|
|
6847
6873
|
const sessionId = row.conversationId;
|
|
6848
|
-
const
|
|
6874
|
+
const lastModel = row.events.at(-1)?.model;
|
|
6849
6875
|
const endedAt = row.ts || row.startedAt;
|
|
6850
6876
|
const push = (partial, topType) => {
|
|
6851
6877
|
lineNumber += 1;
|
|
6852
6878
|
const event = {
|
|
6853
6879
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6854
|
-
source:
|
|
6855
|
-
agent:
|
|
6880
|
+
source: identity.source,
|
|
6881
|
+
agent: identity.agent,
|
|
6856
6882
|
workspaceId,
|
|
6857
6883
|
project,
|
|
6858
|
-
model,
|
|
6884
|
+
model: lastModel,
|
|
6859
6885
|
sessionId,
|
|
6860
6886
|
...partial
|
|
6861
6887
|
};
|
|
@@ -6874,14 +6900,32 @@ function parseCursorCloudAgentSessions(filePath, options, sessions, localIds) {
|
|
|
6874
6900
|
confidence: "partial",
|
|
6875
6901
|
refs: stringRefs({ sourceId: `${sessionId}:started` })
|
|
6876
6902
|
}, "cloud-agent");
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6903
|
+
for (const [index, event] of row.events.entries()) {
|
|
6904
|
+
const item = cloudUsageToPersisted(event, index);
|
|
6905
|
+
const ts = timestampFrom(item.ts) || endedAt;
|
|
6906
|
+
const turnId = `${sessionId}:${item.generationId}`;
|
|
6907
|
+
const model = item.model || lastModel;
|
|
6908
|
+
const nextTs = timestampFrom(row.events[index + 1]?.ts) || endedAt;
|
|
6909
|
+
const completedAt = Date.parse(nextTs) > Date.parse(ts) ? nextTs : ts;
|
|
6910
|
+
push({
|
|
6911
|
+
ts,
|
|
6912
|
+
type: "turn.started",
|
|
6913
|
+
turnId,
|
|
6914
|
+
model,
|
|
6915
|
+
confidence: "partial",
|
|
6916
|
+
refs: stringRefs({ sourceId: `${turnId}:started` })
|
|
6917
|
+
}, "cloud-agent");
|
|
6918
|
+
emitPersistedCursorUsage(push, [item], ts, turnId, model, sessionId);
|
|
6919
|
+
push({
|
|
6920
|
+
ts: completedAt,
|
|
6921
|
+
type: "turn.completed",
|
|
6922
|
+
turnId,
|
|
6923
|
+
model,
|
|
6924
|
+
success: true,
|
|
6925
|
+
confidence: "partial",
|
|
6926
|
+
refs: stringRefs({ sourceId: `${turnId}:completed` })
|
|
6927
|
+
}, "cloud-agent");
|
|
6928
|
+
}
|
|
6885
6929
|
push({
|
|
6886
6930
|
ts: endedAt,
|
|
6887
6931
|
type: "session.ended",
|
|
@@ -6891,7 +6935,7 @@ function parseCursorCloudAgentSessions(filePath, options, sessions, localIds) {
|
|
|
6891
6935
|
}
|
|
6892
6936
|
return events;
|
|
6893
6937
|
}
|
|
6894
|
-
async function parseCursorCloudAgentFile(filePath, options) {
|
|
6938
|
+
async function parseCursorCloudAgentFile(filePath, options, identity) {
|
|
6895
6939
|
const home = stringOption(options.home) || os6.homedir();
|
|
6896
6940
|
const env = {
|
|
6897
6941
|
CURSOR_HOME: process.env.CURSOR_HOME,
|
|
@@ -6899,11 +6943,11 @@ async function parseCursorCloudAgentFile(filePath, options) {
|
|
|
6899
6943
|
};
|
|
6900
6944
|
const injected = injectedCursorCloudUsage(options);
|
|
6901
6945
|
const sessions = injected ? [...injected.entries()].flatMap(([conversationId, events]) => {
|
|
6902
|
-
const
|
|
6903
|
-
return
|
|
6946
|
+
const session = cloudAgentSessionFromEvents(conversationId, events);
|
|
6947
|
+
return session ? [session] : [];
|
|
6904
6948
|
}) : parseCursorCloudUsageCache(await readJsonIfExists(filePath));
|
|
6905
6949
|
const localIds = await listLocalCursorSessionIds(home, env);
|
|
6906
|
-
return parseCursorCloudAgentSessions(filePath, options, sessions, localIds);
|
|
6950
|
+
return parseCursorCloudAgentSessions(filePath, options, sessions, localIds, identity);
|
|
6907
6951
|
}
|
|
6908
6952
|
function decodeKv(value) {
|
|
6909
6953
|
if (typeof value === "string") {
|
|
@@ -7570,7 +7614,6 @@ async function parseCursorTranscriptFile(filePath, options) {
|
|
|
7570
7614
|
push,
|
|
7571
7615
|
sessionId,
|
|
7572
7616
|
options,
|
|
7573
|
-
filePath,
|
|
7574
7617
|
fallbackTs: endedAt || lastTs,
|
|
7575
7618
|
lastTurnId,
|
|
7576
7619
|
model,
|
|
@@ -7584,7 +7627,7 @@ async function appendPersistedCursorUsage(args) {
|
|
|
7584
7627
|
}
|
|
7585
7628
|
emitPersistedCursorUsage(
|
|
7586
7629
|
args.push,
|
|
7587
|
-
await resolveCursorSessionUsage(args.options, args.sessionId
|
|
7630
|
+
await resolveCursorSessionUsage(args.options, args.sessionId),
|
|
7588
7631
|
args.fallbackTs,
|
|
7589
7632
|
args.lastTurnId,
|
|
7590
7633
|
args.model,
|
|
@@ -7673,9 +7716,6 @@ async function collectCursorTranscriptFiles(root, home, skipSessionIds) {
|
|
|
7673
7716
|
}
|
|
7674
7717
|
async function parseCursorSessionFile(filePath, options) {
|
|
7675
7718
|
const base = path13.basename(filePath);
|
|
7676
|
-
if (base === CURSOR_CLOUD_USAGE_FILENAME) {
|
|
7677
|
-
return parseCursorCloudAgentFile(filePath, options);
|
|
7678
|
-
}
|
|
7679
7719
|
if (base.endsWith(".jsonl")) {
|
|
7680
7720
|
return parseCursorTranscriptFile(filePath, options);
|
|
7681
7721
|
}
|
|
@@ -7691,7 +7731,7 @@ async function parseCursorSessionFile(filePath, options) {
|
|
|
7691
7731
|
try {
|
|
7692
7732
|
const composers = listComposers(opened.db);
|
|
7693
7733
|
for (const composer of composers) {
|
|
7694
|
-
const usage = await resolveCursorSessionUsage(options, composer.composerId
|
|
7734
|
+
const usage = await resolveCursorSessionUsage(options, composer.composerId);
|
|
7695
7735
|
events.push(...parseComposer(opened.db, composer, filePath, sourcePathHash, options, usage));
|
|
7696
7736
|
}
|
|
7697
7737
|
} finally {
|
|
@@ -7913,7 +7953,7 @@ function parseComposer(db, composer, filePath, sourcePathHash, options, persiste
|
|
|
7913
7953
|
emitPersistedCursorUsage(push, persistedUsage, endedAt || startedAt, currentTurnId, model, sessionId, events.some((event) => event.type === "model.usage"));
|
|
7914
7954
|
return events;
|
|
7915
7955
|
}
|
|
7916
|
-
async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env
|
|
7956
|
+
async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env) {
|
|
7917
7957
|
if (sourceRoot) {
|
|
7918
7958
|
const info = await stat8(sourceRoot).catch(() => null);
|
|
7919
7959
|
if (!info) {
|
|
@@ -7980,9 +8020,6 @@ async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env, option
|
|
|
7980
8020
|
break;
|
|
7981
8021
|
}
|
|
7982
8022
|
files.push(...await collectCursorTranscriptFiles(cursorProjectsDir(home, env), home, skipIds));
|
|
7983
|
-
if (options) {
|
|
7984
|
-
await appendCursorCloudAgentSource(files, home, env, options);
|
|
7985
|
-
}
|
|
7986
8023
|
return files;
|
|
7987
8024
|
}
|
|
7988
8025
|
function cursorHookConfig() {
|
|
@@ -8027,14 +8064,82 @@ function createCursorAdapter() {
|
|
|
8027
8064
|
};
|
|
8028
8065
|
}
|
|
8029
8066
|
|
|
8067
|
+
// src/adapters/grok-bot.ts
|
|
8068
|
+
import { stat as stat9 } from "node:fs/promises";
|
|
8069
|
+
import os7 from "node:os";
|
|
8070
|
+
var SOURCE_ID2 = "grok-bot";
|
|
8071
|
+
var AGENT_NAME2 = "grok-bot";
|
|
8072
|
+
var IDENTITY = { source: SOURCE_ID2, agent: AGENT_NAME2 };
|
|
8073
|
+
async function grokBotBackfillFiles(sourceRoot, home = os7.homedir(), env, options) {
|
|
8074
|
+
if (sourceRoot) {
|
|
8075
|
+
const info = await stat9(sourceRoot).catch(() => null);
|
|
8076
|
+
return info ? [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }] : [];
|
|
8077
|
+
}
|
|
8078
|
+
if (!options) {
|
|
8079
|
+
return [];
|
|
8080
|
+
}
|
|
8081
|
+
const cachePath = cursorCloudUsageCachePath(home);
|
|
8082
|
+
const map = await loadCursorCloudUsageMap(options);
|
|
8083
|
+
if (map.size === 0) {
|
|
8084
|
+
const info = await stat9(cachePath).catch(() => null);
|
|
8085
|
+
return info ? [{ path: cachePath, modifiedAt: info.mtime.toISOString() }] : [];
|
|
8086
|
+
}
|
|
8087
|
+
const localIds = await listLocalCursorSessionIds(home, env);
|
|
8088
|
+
const cloudOnly = [];
|
|
8089
|
+
for (const [conversationId, events] of map) {
|
|
8090
|
+
if (localIds.has(conversationId)) {
|
|
8091
|
+
continue;
|
|
8092
|
+
}
|
|
8093
|
+
const session = cloudAgentSessionFromEvents(conversationId, events);
|
|
8094
|
+
if (session) {
|
|
8095
|
+
cloudOnly.push(session);
|
|
8096
|
+
}
|
|
8097
|
+
}
|
|
8098
|
+
if (cloudOnly.length === 0) {
|
|
8099
|
+
return [];
|
|
8100
|
+
}
|
|
8101
|
+
return [{
|
|
8102
|
+
path: cachePath,
|
|
8103
|
+
modifiedAt: await writeCursorCloudUsageCache(cachePath, cloudOnly)
|
|
8104
|
+
}];
|
|
8105
|
+
}
|
|
8106
|
+
function createGrokBotAdapter() {
|
|
8107
|
+
return {
|
|
8108
|
+
id: SOURCE_ID2,
|
|
8109
|
+
label: "Grok Bot",
|
|
8110
|
+
agentName: AGENT_NAME2,
|
|
8111
|
+
kind: "agent",
|
|
8112
|
+
// No hooks to install — data arrives via the Cursor dashboard API.
|
|
8113
|
+
// The cache file stands in as the detect/install marker.
|
|
8114
|
+
detectPath(home) {
|
|
8115
|
+
return cursorCloudUsageCachePath(home);
|
|
8116
|
+
},
|
|
8117
|
+
installedPath(home) {
|
|
8118
|
+
return cursorCloudUsageCachePath(home);
|
|
8119
|
+
},
|
|
8120
|
+
async isInstalled() {
|
|
8121
|
+
return false;
|
|
8122
|
+
},
|
|
8123
|
+
installEntries() {
|
|
8124
|
+
return [];
|
|
8125
|
+
},
|
|
8126
|
+
sourcePaths(home) {
|
|
8127
|
+
return [cursorCloudUsageCachePath(home)];
|
|
8128
|
+
},
|
|
8129
|
+
parseSessionFile(filePath, options) {
|
|
8130
|
+
return parseCursorCloudAgentFile(filePath, options, IDENTITY);
|
|
8131
|
+
}
|
|
8132
|
+
};
|
|
8133
|
+
}
|
|
8134
|
+
|
|
8030
8135
|
// src/adapters/grok-build.ts
|
|
8031
|
-
import { readdir as readdir7, readFile as readFile9, stat as
|
|
8136
|
+
import { readdir as readdir7, readFile as readFile9, stat as stat10 } from "node:fs/promises";
|
|
8032
8137
|
import path14 from "node:path";
|
|
8033
8138
|
init_fs();
|
|
8034
8139
|
var GROK_COST_TICKS_PER_USD = 1e9;
|
|
8035
|
-
var
|
|
8036
|
-
var
|
|
8037
|
-
var HOOK_COMMAND2 = `vibetime hook --agent ${
|
|
8140
|
+
var SOURCE_ID3 = "grok-build";
|
|
8141
|
+
var AGENT_NAME3 = "grok-build";
|
|
8142
|
+
var HOOK_COMMAND2 = `vibetime hook --agent ${SOURCE_ID3}`;
|
|
8038
8143
|
function grokHome(home, env) {
|
|
8039
8144
|
const override = env?.GROK_HOME;
|
|
8040
8145
|
if (override && override.trim()) {
|
|
@@ -8068,7 +8173,7 @@ async function grokBackfillFiles(sourceRoot, home, env) {
|
|
|
8068
8173
|
continue;
|
|
8069
8174
|
}
|
|
8070
8175
|
try {
|
|
8071
|
-
const info = await
|
|
8176
|
+
const info = await stat10(entryPath);
|
|
8072
8177
|
files.push({ path: entryPath, modifiedAt: info.mtime.toISOString() });
|
|
8073
8178
|
} catch {
|
|
8074
8179
|
}
|
|
@@ -8120,8 +8225,8 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
8120
8225
|
};
|
|
8121
8226
|
const base = (partial) => ({
|
|
8122
8227
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
8123
|
-
source:
|
|
8124
|
-
agent:
|
|
8228
|
+
source: SOURCE_ID3,
|
|
8229
|
+
agent: AGENT_NAME3,
|
|
8125
8230
|
workspaceId,
|
|
8126
8231
|
project,
|
|
8127
8232
|
cwd,
|
|
@@ -8681,7 +8786,7 @@ async function loadHunkFileActivities(hunkPath, cwd) {
|
|
|
8681
8786
|
}
|
|
8682
8787
|
return activities;
|
|
8683
8788
|
}
|
|
8684
|
-
var grokHandler = (msg) => hookHandler(
|
|
8789
|
+
var grokHandler = (msg) => hookHandler(SOURCE_ID3, msg);
|
|
8685
8790
|
function hookConfig5() {
|
|
8686
8791
|
const anyTool = [{ matcher: ".*", hooks: [grokHandler("Reporting tool activity")] }];
|
|
8687
8792
|
return {
|
|
@@ -8703,9 +8808,9 @@ function hookConfig5() {
|
|
|
8703
8808
|
}
|
|
8704
8809
|
function createGrokBuildAdapter() {
|
|
8705
8810
|
return {
|
|
8706
|
-
id:
|
|
8811
|
+
id: SOURCE_ID3,
|
|
8707
8812
|
label: "Grok Build",
|
|
8708
|
-
agentName:
|
|
8813
|
+
agentName: AGENT_NAME3,
|
|
8709
8814
|
kind: "agent",
|
|
8710
8815
|
detectPath(home, env) {
|
|
8711
8816
|
return grokHome(home, env);
|
|
@@ -8736,6 +8841,36 @@ import path15 from "node:path";
|
|
|
8736
8841
|
|
|
8737
8842
|
// src/lib/toml-hooks.ts
|
|
8738
8843
|
init_fs();
|
|
8844
|
+
var KIMI_CODE_HOOK_EVENTS = /* @__PURE__ */ new Set([
|
|
8845
|
+
"PreToolUse",
|
|
8846
|
+
"PostToolUse",
|
|
8847
|
+
"PostToolUseFailure",
|
|
8848
|
+
"PermissionRequest",
|
|
8849
|
+
"PermissionResult",
|
|
8850
|
+
"UserPromptSubmit",
|
|
8851
|
+
"Stop",
|
|
8852
|
+
"StopFailure",
|
|
8853
|
+
"Interrupt",
|
|
8854
|
+
"SessionStart",
|
|
8855
|
+
"SessionEnd",
|
|
8856
|
+
"SubagentStart",
|
|
8857
|
+
"SubagentStop",
|
|
8858
|
+
"PreCompact",
|
|
8859
|
+
"PostCompact",
|
|
8860
|
+
"Notification"
|
|
8861
|
+
]);
|
|
8862
|
+
function isValidTomlHookRule(rule) {
|
|
8863
|
+
if (!KIMI_CODE_HOOK_EVENTS.has(rule.event)) {
|
|
8864
|
+
return false;
|
|
8865
|
+
}
|
|
8866
|
+
if (typeof rule.command !== "string" || rule.command.trim() === "") {
|
|
8867
|
+
return false;
|
|
8868
|
+
}
|
|
8869
|
+
if (rule.timeout !== void 0 && (!Number.isInteger(rule.timeout) || rule.timeout < 1 || rule.timeout > 600)) {
|
|
8870
|
+
return false;
|
|
8871
|
+
}
|
|
8872
|
+
return true;
|
|
8873
|
+
}
|
|
8739
8874
|
async function hasTomlHookCommand(filePath, command) {
|
|
8740
8875
|
const text = await readTextIfExists(filePath);
|
|
8741
8876
|
if (!text) {
|
|
@@ -8767,8 +8902,9 @@ function parseTomlHookRules(text) {
|
|
|
8767
8902
|
return rules;
|
|
8768
8903
|
}
|
|
8769
8904
|
function mergeTomlHookRules(existingText, desired) {
|
|
8905
|
+
const valid = desired.filter(isValidTomlHookRule);
|
|
8770
8906
|
const existingKeys = new Set(parseTomlHookRules(existingText).map(ruleKey));
|
|
8771
|
-
const toAppend =
|
|
8907
|
+
const toAppend = valid.filter((rule) => !existingKeys.has(ruleKey(rule)));
|
|
8772
8908
|
if (toAppend.length === 0) {
|
|
8773
8909
|
return existingText;
|
|
8774
8910
|
}
|
|
@@ -8899,7 +9035,6 @@ function kimiHookRules() {
|
|
|
8899
9035
|
"SessionStart",
|
|
8900
9036
|
"SessionEnd",
|
|
8901
9037
|
"UserPromptSubmit",
|
|
8902
|
-
"TurnStarted",
|
|
8903
9038
|
"PostToolUse",
|
|
8904
9039
|
"PostToolUseFailure",
|
|
8905
9040
|
"Stop",
|
|
@@ -9372,7 +9507,7 @@ function createKimiCodeAdapter() {
|
|
|
9372
9507
|
}
|
|
9373
9508
|
|
|
9374
9509
|
// src/adapters/opencode.ts
|
|
9375
|
-
import
|
|
9510
|
+
import os8 from "node:os";
|
|
9376
9511
|
import path16 from "node:path";
|
|
9377
9512
|
async function parseOpenCodeSessionFile(dbPath, options) {
|
|
9378
9513
|
const { DatabaseSync } = await import("node:sqlite");
|
|
@@ -9827,20 +9962,20 @@ function opencodeDataCandidates(home, env) {
|
|
|
9827
9962
|
const primary = xdgData && xdgData.trim() ? path16.join(path16.resolve(xdgData), "opencode", "opencode.db") : path16.join(home, ".local", "share", "opencode", "opencode.db");
|
|
9828
9963
|
return [primary, path16.join(home, ".opencode", "opencode.db")];
|
|
9829
9964
|
}
|
|
9830
|
-
async function opencodeBackfillFiles(sourceRoot, home =
|
|
9831
|
-
const { stat:
|
|
9965
|
+
async function opencodeBackfillFiles(sourceRoot, home = os8.homedir(), env) {
|
|
9966
|
+
const { stat: stat18 } = await import("node:fs/promises");
|
|
9832
9967
|
if (sourceRoot) {
|
|
9833
9968
|
if (!sourceRoot.endsWith(".db")) {
|
|
9834
9969
|
return [];
|
|
9835
9970
|
}
|
|
9836
|
-
const info = await
|
|
9971
|
+
const info = await stat18(sourceRoot).catch(() => null);
|
|
9837
9972
|
if (!info) {
|
|
9838
9973
|
return [];
|
|
9839
9974
|
}
|
|
9840
9975
|
return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
|
|
9841
9976
|
}
|
|
9842
9977
|
for (const candidatePath of opencodeDataCandidates(home, env)) {
|
|
9843
|
-
const info = await
|
|
9978
|
+
const info = await stat18(candidatePath).catch(() => null);
|
|
9844
9979
|
if (info) {
|
|
9845
9980
|
return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
|
|
9846
9981
|
}
|
|
@@ -9940,8 +10075,8 @@ function createOpenCodeAdapter() {
|
|
|
9940
10075
|
}
|
|
9941
10076
|
|
|
9942
10077
|
// src/adapters/pi.ts
|
|
9943
|
-
import { readdir as readdir8, readFile as readFile11, stat as
|
|
9944
|
-
import
|
|
10078
|
+
import { readdir as readdir8, readFile as readFile11, stat as stat11 } from "node:fs/promises";
|
|
10079
|
+
import os9 from "node:os";
|
|
9945
10080
|
import path17 from "node:path";
|
|
9946
10081
|
init_fs();
|
|
9947
10082
|
function piHostSessionIdFromBasename(basename) {
|
|
@@ -10352,7 +10487,7 @@ async function parsePiWorkflowRunFile(filePath, options) {
|
|
|
10352
10487
|
return state.events.filter((event) => matchesBackfillFilters(event, options));
|
|
10353
10488
|
}
|
|
10354
10489
|
async function findPiSessionFileById(sessionId, options) {
|
|
10355
|
-
const sessionsDir = piSessionDir(path17.resolve(stringOption(options.home) ||
|
|
10490
|
+
const sessionsDir = piSessionDir(path17.resolve(stringOption(options.home) || os9.homedir()));
|
|
10356
10491
|
try {
|
|
10357
10492
|
const projectDirs = await readdir8(sessionsDir, { withFileTypes: true });
|
|
10358
10493
|
for (const dir of projectDirs) {
|
|
@@ -10629,14 +10764,14 @@ async function workflowRunGroupId(filePath) {
|
|
|
10629
10764
|
return void 0;
|
|
10630
10765
|
}
|
|
10631
10766
|
}
|
|
10632
|
-
async function piBackfillFiles(sourceRoot, home =
|
|
10767
|
+
async function piBackfillFiles(sourceRoot, home = os9.homedir(), env) {
|
|
10633
10768
|
const lists = sourceRoot ? [await listFilesByExtensions(sourceRoot, [".jsonl", ".json"])] : await Promise.all([
|
|
10634
10769
|
listFilesByExtensions(piSessionDir(home, env), [".jsonl"]),
|
|
10635
10770
|
listFilesByExtensions(piWorkflowProjectsDir(home), [".json"])
|
|
10636
10771
|
]);
|
|
10637
10772
|
const files = lists.flat().sort();
|
|
10638
10773
|
return Promise.all(files.map(async (filePath) => {
|
|
10639
|
-
const info = await
|
|
10774
|
+
const info = await stat11(filePath);
|
|
10640
10775
|
let groupId = resolvePiBackfillGroupId(filePath);
|
|
10641
10776
|
if (!groupId && filePath.endsWith(".json")) {
|
|
10642
10777
|
groupId = await workflowRunGroupId(filePath);
|
|
@@ -10683,13 +10818,13 @@ function createPiAdapter() {
|
|
|
10683
10818
|
}
|
|
10684
10819
|
|
|
10685
10820
|
// src/adapters/qoder-cn.ts
|
|
10686
|
-
import { readdir as readdir9, readFile as readFile12, stat as
|
|
10687
|
-
import
|
|
10821
|
+
import { readdir as readdir9, readFile as readFile12, stat as stat12 } from "node:fs/promises";
|
|
10822
|
+
import os11 from "node:os";
|
|
10688
10823
|
import path19 from "node:path";
|
|
10689
10824
|
|
|
10690
10825
|
// src/adapters/qoder-local-db.ts
|
|
10691
10826
|
import { access } from "node:fs/promises";
|
|
10692
|
-
import
|
|
10827
|
+
import os10 from "node:os";
|
|
10693
10828
|
import path18 from "node:path";
|
|
10694
10829
|
function takeQoderDbModelCall(calls, requestId, blockStart) {
|
|
10695
10830
|
if (requestId) {
|
|
@@ -10707,7 +10842,7 @@ function takeQoderDbModelCall(calls, requestId, blockStart) {
|
|
|
10707
10842
|
}
|
|
10708
10843
|
return calls.ordered.shift();
|
|
10709
10844
|
}
|
|
10710
|
-
function appDataRoot(appDirName, home =
|
|
10845
|
+
function appDataRoot(appDirName, home = os10.homedir()) {
|
|
10711
10846
|
if (process.platform === "darwin") {
|
|
10712
10847
|
return path18.join(home, "Library", "Application Support", appDirName);
|
|
10713
10848
|
}
|
|
@@ -11010,7 +11145,7 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
11010
11145
|
let cwd;
|
|
11011
11146
|
let project = projectContext.project;
|
|
11012
11147
|
let model;
|
|
11013
|
-
const home = path19.resolve(stringOption(options.home) ||
|
|
11148
|
+
const home = path19.resolve(stringOption(options.home) || os11.homedir());
|
|
11014
11149
|
const modelMap = await loadQoderCnModelNames(configDir2, home);
|
|
11015
11150
|
const isSubagentSession = filePath.includes("subagents");
|
|
11016
11151
|
const segmentModelCalls = await loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap);
|
|
@@ -11580,7 +11715,7 @@ async function gitRootFromCwds2(cwds) {
|
|
|
11580
11715
|
while (!seen.has(current)) {
|
|
11581
11716
|
seen.add(current);
|
|
11582
11717
|
try {
|
|
11583
|
-
await
|
|
11718
|
+
await stat12(path19.join(current, ".git"));
|
|
11584
11719
|
return current;
|
|
11585
11720
|
} catch {
|
|
11586
11721
|
}
|
|
@@ -11614,7 +11749,7 @@ function encodeQoderCnProjectPath(value) {
|
|
|
11614
11749
|
}
|
|
11615
11750
|
async function qoderCnProjectFromFilePath(filePath, options) {
|
|
11616
11751
|
const projectDir = path19.basename(path19.dirname(filePath));
|
|
11617
|
-
const home = options ? path19.resolve(stringOption(options.home) ||
|
|
11752
|
+
const home = options ? path19.resolve(stringOption(options.home) || os11.homedir()) : os11.homedir();
|
|
11618
11753
|
const resolved = await resolveQoderCnProjectPath(projectDir, home);
|
|
11619
11754
|
if (resolved) {
|
|
11620
11755
|
return path19.basename(resolved);
|
|
@@ -11730,8 +11865,8 @@ function createQoderCnAdapter() {
|
|
|
11730
11865
|
|
|
11731
11866
|
// src/adapters/qoder.ts
|
|
11732
11867
|
import { existsSync } from "node:fs";
|
|
11733
|
-
import { readdir as readdir10, readFile as readFile13, stat as
|
|
11734
|
-
import
|
|
11868
|
+
import { readdir as readdir10, readFile as readFile13, stat as stat13 } from "node:fs/promises";
|
|
11869
|
+
import os12 from "node:os";
|
|
11735
11870
|
import path20 from "node:path";
|
|
11736
11871
|
function parseQoderPaths(filePath) {
|
|
11737
11872
|
const parts = filePath.split(path20.sep);
|
|
@@ -11890,7 +12025,7 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
11890
12025
|
let cwd;
|
|
11891
12026
|
let project = projectContext.project;
|
|
11892
12027
|
let model;
|
|
11893
|
-
const home = path20.resolve(stringOption(options.home) ||
|
|
12028
|
+
const home = path20.resolve(stringOption(options.home) || os12.homedir());
|
|
11894
12029
|
const modelMap = await loadQoderModelNames(configDir2, home);
|
|
11895
12030
|
const qwenworkRoot = isQwenworkConfigRoot(configDir2);
|
|
11896
12031
|
const isSubagentSession = filePath.includes("subagents");
|
|
@@ -12426,7 +12561,7 @@ async function gitRootFromCwds3(cwds) {
|
|
|
12426
12561
|
while (!seen.has(current)) {
|
|
12427
12562
|
seen.add(current);
|
|
12428
12563
|
try {
|
|
12429
|
-
await
|
|
12564
|
+
await stat13(path20.join(current, ".git"));
|
|
12430
12565
|
return current;
|
|
12431
12566
|
} catch {
|
|
12432
12567
|
}
|
|
@@ -12478,7 +12613,7 @@ function qoderEncodedProjectSuffix(projectDir, home) {
|
|
|
12478
12613
|
}
|
|
12479
12614
|
async function qoderProjectFromFilePath(filePath, options) {
|
|
12480
12615
|
const projectDir = path20.basename(path20.dirname(filePath));
|
|
12481
|
-
const home = options ? path20.resolve(stringOption(options.home) ||
|
|
12616
|
+
const home = options ? path20.resolve(stringOption(options.home) || os12.homedir()) : os12.homedir();
|
|
12482
12617
|
const resolved = await resolveQoderProjectPath(projectDir, home);
|
|
12483
12618
|
if (resolved) {
|
|
12484
12619
|
return path20.basename(resolved);
|
|
@@ -12634,7 +12769,7 @@ function normalizeId(id) {
|
|
|
12634
12769
|
}
|
|
12635
12770
|
|
|
12636
12771
|
// src/adapters/workbuddy.ts
|
|
12637
|
-
import { readdir as readdir11, readFile as readFile14, stat as
|
|
12772
|
+
import { readdir as readdir11, readFile as readFile14, stat as stat14 } from "node:fs/promises";
|
|
12638
12773
|
import path21 from "node:path";
|
|
12639
12774
|
function workbuddyProjectsDir(home, env) {
|
|
12640
12775
|
const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
|
|
@@ -13087,7 +13222,7 @@ async function workbuddyBackfillFiles(sourceRoot, home, env) {
|
|
|
13087
13222
|
for (const entry of entries) {
|
|
13088
13223
|
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
13089
13224
|
const filePath = path21.join(projectDir, entry.name);
|
|
13090
|
-
const info = await
|
|
13225
|
+
const info = await stat14(filePath);
|
|
13091
13226
|
files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
|
|
13092
13227
|
}
|
|
13093
13228
|
}
|
|
@@ -13148,7 +13283,7 @@ function createWorkbuddyAdapter() {
|
|
|
13148
13283
|
|
|
13149
13284
|
// src/adapters/zcode.ts
|
|
13150
13285
|
import { execFile } from "node:child_process";
|
|
13151
|
-
import { readFile as readFile15, stat as
|
|
13286
|
+
import { readFile as readFile15, stat as stat15 } from "node:fs/promises";
|
|
13152
13287
|
import path22 from "node:path";
|
|
13153
13288
|
import { promisify as promisify2 } from "node:util";
|
|
13154
13289
|
var execFileAsync = promisify2(execFile);
|
|
@@ -13187,7 +13322,7 @@ var providerNameCache = null;
|
|
|
13187
13322
|
async function loadProviderNames(configPath2) {
|
|
13188
13323
|
let fileMtime = 0;
|
|
13189
13324
|
try {
|
|
13190
|
-
const info = await
|
|
13325
|
+
const info = await stat15(configPath2);
|
|
13191
13326
|
fileMtime = info.mtimeMs;
|
|
13192
13327
|
} catch {
|
|
13193
13328
|
return /* @__PURE__ */ new Map();
|
|
@@ -13415,7 +13550,7 @@ async function parseZCodeDb(filePath, options) {
|
|
|
13415
13550
|
for (let i = 0; i < 12; i++) {
|
|
13416
13551
|
const probe = path22.join(candidate, ".zcode", "v2", "config.json");
|
|
13417
13552
|
try {
|
|
13418
|
-
await
|
|
13553
|
+
await stat15(probe);
|
|
13419
13554
|
configPath2 = probe;
|
|
13420
13555
|
break;
|
|
13421
13556
|
} catch {
|
|
@@ -13649,11 +13784,11 @@ async function zcodeBackfillFiles(sourceRoot, home, env) {
|
|
|
13649
13784
|
const candidate = sourceRoot || zcodeDbPath(home, env);
|
|
13650
13785
|
const filePath = candidate.endsWith(".sqlite") ? candidate : path22.join(candidate, "db", "db.sqlite");
|
|
13651
13786
|
try {
|
|
13652
|
-
const info = await
|
|
13787
|
+
const info = await stat15(filePath);
|
|
13653
13788
|
let modifiedMs = info.mtimeMs;
|
|
13654
13789
|
for (const suffix of ["-wal", "-shm"]) {
|
|
13655
13790
|
try {
|
|
13656
|
-
const sidecar = await
|
|
13791
|
+
const sidecar = await stat15(`${filePath}${suffix}`);
|
|
13657
13792
|
modifiedMs = Math.max(modifiedMs, sidecar.mtimeMs);
|
|
13658
13793
|
} catch {
|
|
13659
13794
|
}
|
|
@@ -13696,7 +13831,7 @@ function createZCodeAdapter() {
|
|
|
13696
13831
|
}
|
|
13697
13832
|
|
|
13698
13833
|
// src/adapters/zed.ts
|
|
13699
|
-
import
|
|
13834
|
+
import os13 from "node:os";
|
|
13700
13835
|
import path23 from "node:path";
|
|
13701
13836
|
function zedThreadsCandidates(home, env) {
|
|
13702
13837
|
const candidates = [];
|
|
@@ -14048,20 +14183,20 @@ async function parseZedSessionFile(dbPath, options) {
|
|
|
14048
14183
|
}
|
|
14049
14184
|
return events.filter((event) => matchesBackfillFilters(event, options));
|
|
14050
14185
|
}
|
|
14051
|
-
async function zedBackfillFiles(sourceRoot, home =
|
|
14052
|
-
const { stat:
|
|
14186
|
+
async function zedBackfillFiles(sourceRoot, home = os13.homedir(), env) {
|
|
14187
|
+
const { stat: stat18 } = await import("node:fs/promises");
|
|
14053
14188
|
if (sourceRoot) {
|
|
14054
14189
|
if (!sourceRoot.endsWith(".db")) {
|
|
14055
14190
|
return [];
|
|
14056
14191
|
}
|
|
14057
|
-
const info = await
|
|
14192
|
+
const info = await stat18(sourceRoot).catch(() => null);
|
|
14058
14193
|
if (!info) {
|
|
14059
14194
|
return [];
|
|
14060
14195
|
}
|
|
14061
14196
|
return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
|
|
14062
14197
|
}
|
|
14063
14198
|
for (const candidatePath of zedThreadsCandidates(home, env)) {
|
|
14064
|
-
const info = await
|
|
14199
|
+
const info = await stat18(candidatePath).catch(() => null);
|
|
14065
14200
|
if (info) {
|
|
14066
14201
|
return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
|
|
14067
14202
|
}
|
|
@@ -14664,21 +14799,15 @@ async function mergeHooksToml(filePath, content, { dryRun, force, onWrite }) {
|
|
|
14664
14799
|
);
|
|
14665
14800
|
}
|
|
14666
14801
|
const desired = Array.isArray(content.hooks) ? content.hooks : [];
|
|
14667
|
-
const
|
|
14802
|
+
const commands = [...new Set(
|
|
14803
|
+
desired.map((rule) => rule.command).filter((cmd) => typeof cmd === "string" && cmd.length > 0)
|
|
14804
|
+
)];
|
|
14805
|
+
const stripped = removeTomlHookRulesByCommand(existingText ?? "", commands);
|
|
14806
|
+
const nextText = mergeTomlHookRules(stripped, desired);
|
|
14668
14807
|
if (existingText === nextText || existingText === null && nextText === "") {
|
|
14669
14808
|
onWrite(`Already installed ${filePath}`);
|
|
14670
14809
|
return;
|
|
14671
14810
|
}
|
|
14672
|
-
if (existingText !== null) {
|
|
14673
|
-
const existingKeys = new Set(
|
|
14674
|
-
parseTomlHookRules(existingText).map((rule) => `${rule.event}\0${rule.command}`)
|
|
14675
|
-
);
|
|
14676
|
-
const allPresent = desired.every((rule) => existingKeys.has(`${rule.event}\0${rule.command}`));
|
|
14677
|
-
if (allPresent && desired.length > 0) {
|
|
14678
|
-
onWrite(`Already installed ${filePath}`);
|
|
14679
|
-
return;
|
|
14680
|
-
}
|
|
14681
|
-
}
|
|
14682
14811
|
await mkdir7(pathMod.dirname(filePath), { recursive: true });
|
|
14683
14812
|
await writeFile6(filePath, nextText, "utf8");
|
|
14684
14813
|
onWrite(`Installed ${filePath}`);
|
|
@@ -14958,7 +15087,7 @@ function defaultMachineName() {
|
|
|
14958
15087
|
init_fs();
|
|
14959
15088
|
|
|
14960
15089
|
// src/lib/logger.ts
|
|
14961
|
-
import { appendFile, mkdir as mkdir5, rename, stat as
|
|
15090
|
+
import { appendFile, mkdir as mkdir5, rename, stat as stat16 } from "node:fs/promises";
|
|
14962
15091
|
import { homedir as homedir2 } from "node:os";
|
|
14963
15092
|
import path25 from "node:path";
|
|
14964
15093
|
var MAX_BYTES = 1 * 1024 * 1024;
|
|
@@ -14976,7 +15105,7 @@ function serializeError(error) {
|
|
|
14976
15105
|
}
|
|
14977
15106
|
async function rotateIfNeeded(file) {
|
|
14978
15107
|
try {
|
|
14979
|
-
const info = await
|
|
15108
|
+
const info = await stat16(file);
|
|
14980
15109
|
if (info.size > MAX_BYTES) {
|
|
14981
15110
|
await rename(file, `${file}.1`).catch(() => {
|
|
14982
15111
|
});
|
|
@@ -15212,7 +15341,7 @@ async function deleteMachine(remote, id) {
|
|
|
15212
15341
|
}
|
|
15213
15342
|
|
|
15214
15343
|
// src/lib/types.ts
|
|
15215
|
-
var BACKFILL_STATE_SCHEMA_VERSION =
|
|
15344
|
+
var BACKFILL_STATE_SCHEMA_VERSION = 10;
|
|
15216
15345
|
|
|
15217
15346
|
// src/cli.ts
|
|
15218
15347
|
function createRegistry() {
|
|
@@ -15233,6 +15362,7 @@ function createRegistry() {
|
|
|
15233
15362
|
registry.register(createZedAdapter());
|
|
15234
15363
|
registry.register(createKimiCodeAdapter());
|
|
15235
15364
|
registry.register(createCursorAdapter());
|
|
15365
|
+
registry.register(createGrokBotAdapter());
|
|
15236
15366
|
return registry;
|
|
15237
15367
|
}
|
|
15238
15368
|
var defaultContext = {
|
|
@@ -15792,7 +15922,10 @@ async function listBackfillSourceFiles(source, options, ctx) {
|
|
|
15792
15922
|
return zedBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
15793
15923
|
}
|
|
15794
15924
|
if (source.id === "cursor") {
|
|
15795
|
-
return cursorBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env
|
|
15925
|
+
return cursorBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
15926
|
+
}
|
|
15927
|
+
if (source.id === "grok-bot") {
|
|
15928
|
+
return grokBotBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env, options);
|
|
15796
15929
|
}
|
|
15797
15930
|
if (source.id === "pi") {
|
|
15798
15931
|
return piBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
@@ -15801,7 +15934,7 @@ async function listBackfillSourceFiles(source, options, ctx) {
|
|
|
15801
15934
|
const fileLists = await Promise.all(roots.map((r) => listJsonlFiles(r)));
|
|
15802
15935
|
const files = fileLists.flat().sort().slice(0, numberOption(options.limit) || void 0);
|
|
15803
15936
|
return Promise.all(files.map(async (filePath) => {
|
|
15804
|
-
const info = await
|
|
15937
|
+
const info = await stat17(filePath);
|
|
15805
15938
|
return { path: filePath, modifiedAt: info.mtime.toISOString() };
|
|
15806
15939
|
}));
|
|
15807
15940
|
}
|
|
@@ -16353,7 +16486,7 @@ function syncLocalRunnerEntryArgs(cliPath) {
|
|
|
16353
16486
|
return [path26.resolve(path26.dirname(cliPath), "../bin/vibetime.mjs")];
|
|
16354
16487
|
}
|
|
16355
16488
|
function resolveHome3(options, ctx) {
|
|
16356
|
-
return path26.resolve(stringOption(options.home) || ctx.env.HOME ||
|
|
16489
|
+
return path26.resolve(stringOption(options.home) || ctx.env.HOME || os14.homedir());
|
|
16357
16490
|
}
|
|
16358
16491
|
function requestedTargets(options) {
|
|
16359
16492
|
const value = options.target || options.targets;
|
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.65",
|
|
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": {
|