@yhong91/vibetime 0.1.68 → 0.1.70
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 +93 -48
- package/package.json +1 -1
package/bin/vibetime.mjs
CHANGED
|
@@ -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.70" : "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;
|
|
@@ -5024,7 +5024,7 @@ function fileActivitiesFromPatchChanges(changes, ts, cwd, displayFilePath3) {
|
|
|
5024
5024
|
}
|
|
5025
5025
|
|
|
5026
5026
|
// src/lib/session-context.ts
|
|
5027
|
-
import { mkdir as mkdir3, stat as stat6, writeFile as writeFile3 } from "node:fs/promises";
|
|
5027
|
+
import { mkdir as mkdir3, rename, stat as stat6, unlink, writeFile as writeFile3 } from "node:fs/promises";
|
|
5028
5028
|
import os4 from "node:os";
|
|
5029
5029
|
import path10 from "node:path";
|
|
5030
5030
|
init_fs();
|
|
@@ -5039,7 +5039,12 @@ function resolveHome2(options) {
|
|
|
5039
5039
|
return options ? path10.resolve(stringOption(options.home) || os4.homedir()) : os4.homedir();
|
|
5040
5040
|
}
|
|
5041
5041
|
async function readPersistedSessionContext(home, sessionId) {
|
|
5042
|
-
|
|
5042
|
+
let raw;
|
|
5043
|
+
try {
|
|
5044
|
+
raw = await readJsonIfExists(sessionContextPath(home, sessionId));
|
|
5045
|
+
} catch {
|
|
5046
|
+
return void 0;
|
|
5047
|
+
}
|
|
5043
5048
|
if (!isPlainObject(raw)) {
|
|
5044
5049
|
return void 0;
|
|
5045
5050
|
}
|
|
@@ -5086,8 +5091,17 @@ async function persistHookSessionContext(home, payload) {
|
|
|
5086
5091
|
usage: nextUsage
|
|
5087
5092
|
};
|
|
5088
5093
|
await mkdir3(sessionContextDir(home), { recursive: true });
|
|
5089
|
-
|
|
5094
|
+
const dest = sessionContextPath(home, sessionId);
|
|
5095
|
+
const tmp = `${dest}.${process.pid}.tmp`;
|
|
5096
|
+
await writeFile3(tmp, `${JSON.stringify(context, null, 2)}
|
|
5090
5097
|
`, "utf8");
|
|
5098
|
+
try {
|
|
5099
|
+
await rename(tmp, dest);
|
|
5100
|
+
} catch (error) {
|
|
5101
|
+
await unlink(tmp).catch(() => {
|
|
5102
|
+
});
|
|
5103
|
+
throw error;
|
|
5104
|
+
}
|
|
5091
5105
|
}
|
|
5092
5106
|
function hookSessionId(payload) {
|
|
5093
5107
|
for (const key of ["session_id", "sessionId", "conversation_id", "conversationId"]) {
|
|
@@ -6499,6 +6513,22 @@ function cloudAgentSessionFromEvents(conversationId, events) {
|
|
|
6499
6513
|
events: sorted.map((event) => ({ ...event, conversationId }))
|
|
6500
6514
|
};
|
|
6501
6515
|
}
|
|
6516
|
+
function hookUncachedInputTokens(turn) {
|
|
6517
|
+
return Math.max(0, (turn.tokensInput || 0) - (turn.tokensCacheReadInput || 0));
|
|
6518
|
+
}
|
|
6519
|
+
function cloudEventMatchesHookTurn(event, turn) {
|
|
6520
|
+
return event.tokensInput === hookUncachedInputTokens(turn) && event.tokensOutput === (turn.tokensOutput || 0) && event.tokensCacheReadInput === (turn.tokensCacheReadInput || 0);
|
|
6521
|
+
}
|
|
6522
|
+
function unmatchedCloudEvents(events, turns) {
|
|
6523
|
+
const used = /* @__PURE__ */ new Set();
|
|
6524
|
+
for (const turn of turns) {
|
|
6525
|
+
const index = events.findIndex((event, i) => !used.has(i) && cloudEventMatchesHookTurn(event, turn));
|
|
6526
|
+
if (index >= 0) {
|
|
6527
|
+
used.add(index);
|
|
6528
|
+
}
|
|
6529
|
+
}
|
|
6530
|
+
return events.filter((_, i) => !used.has(i));
|
|
6531
|
+
}
|
|
6502
6532
|
async function fetchCursorDashboardUsage(args) {
|
|
6503
6533
|
const cookie = cursorSessionCookie(args.accessToken);
|
|
6504
6534
|
if (!cookie) {
|
|
@@ -6742,6 +6772,13 @@ function injectedCloudEvent(conversationId, item) {
|
|
|
6742
6772
|
tokensCacheCreationInput
|
|
6743
6773
|
};
|
|
6744
6774
|
}
|
|
6775
|
+
function cloudUsageMapFromSessions(sessions) {
|
|
6776
|
+
const map = /* @__PURE__ */ new Map();
|
|
6777
|
+
for (const session of sessions) {
|
|
6778
|
+
map.set(session.conversationId, session.events);
|
|
6779
|
+
}
|
|
6780
|
+
return map;
|
|
6781
|
+
}
|
|
6745
6782
|
function injectedCursorCloudUsage(options) {
|
|
6746
6783
|
if (!Object.prototype.hasOwnProperty.call(options, "cursorCloudUsage")) {
|
|
6747
6784
|
return void 0;
|
|
@@ -6827,7 +6864,9 @@ async function loadCursorCloudUsageMap(options, filePath) {
|
|
|
6827
6864
|
if (intervalSeconds > 0 && !options.force) {
|
|
6828
6865
|
const lastFetchedMs = await readLastCloudFetchAt(home);
|
|
6829
6866
|
if (lastFetchedMs !== void 0 && Date.now() - lastFetchedMs < intervalSeconds * 1e3) {
|
|
6830
|
-
return
|
|
6867
|
+
return cloudUsageMapFromSessions(
|
|
6868
|
+
parseCursorCloudUsageCache(await readJsonIfExists(cursorCloudUsageCachePath(home)))
|
|
6869
|
+
);
|
|
6831
6870
|
}
|
|
6832
6871
|
}
|
|
6833
6872
|
const pending = (async () => {
|
|
@@ -6877,15 +6916,13 @@ function cloudUsageToPersisted(row, index = 0) {
|
|
|
6877
6916
|
async function resolveCursorSessionUsage(options, sessionId, filePath) {
|
|
6878
6917
|
const persisted = await readPersistedSessionContextFromOptions(options, sessionId);
|
|
6879
6918
|
const hookUsage = persisted?.usage ?? [];
|
|
6880
|
-
if (hookUsage.length > 0) {
|
|
6881
|
-
return hookUsage;
|
|
6882
|
-
}
|
|
6883
6919
|
const cloudMap = await loadCursorCloudUsageMap(options, filePath);
|
|
6884
|
-
const cloudEvents = cloudMap.get(sessionId);
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6920
|
+
const cloudEvents = cloudMap.get(sessionId) ?? [];
|
|
6921
|
+
const leftover = unmatchedCloudEvents(cloudEvents, hookUsage);
|
|
6922
|
+
return [
|
|
6923
|
+
...hookUsage,
|
|
6924
|
+
...leftover.map((event, index) => cloudUsageToPersisted(event, index))
|
|
6925
|
+
];
|
|
6889
6926
|
}
|
|
6890
6927
|
function cursorCloudUsageCachePath(home) {
|
|
6891
6928
|
return path13.join(home, ".vibetime", CURSOR_CLOUD_USAGE_FILENAME);
|
|
@@ -7082,10 +7119,10 @@ async function openCursorDb(dbPath) {
|
|
|
7082
7119
|
return {
|
|
7083
7120
|
db: new DatabaseSync(tmp, { readOnly: true }),
|
|
7084
7121
|
cleanup: async () => {
|
|
7085
|
-
const { unlink } = await import("node:fs/promises");
|
|
7086
|
-
await
|
|
7087
|
-
await
|
|
7088
|
-
await
|
|
7122
|
+
const { unlink: unlink2 } = await import("node:fs/promises");
|
|
7123
|
+
await unlink2(tmp).catch(() => void 0);
|
|
7124
|
+
await unlink2(`${tmp}-wal`).catch(() => void 0);
|
|
7125
|
+
await unlink2(`${tmp}-shm`).catch(() => void 0);
|
|
7089
7126
|
}
|
|
7090
7127
|
};
|
|
7091
7128
|
}
|
|
@@ -8241,7 +8278,7 @@ import os7 from "node:os";
|
|
|
8241
8278
|
var SOURCE_ID2 = "grok-bot";
|
|
8242
8279
|
var AGENT_NAME2 = "grok-bot";
|
|
8243
8280
|
var IDENTITY = { source: SOURCE_ID2, agent: AGENT_NAME2 };
|
|
8244
|
-
async function grokBotBackfillFiles(sourceRoot, home = os7.homedir(),
|
|
8281
|
+
async function grokBotBackfillFiles(sourceRoot, home = os7.homedir(), _env, options) {
|
|
8245
8282
|
if (sourceRoot) {
|
|
8246
8283
|
const info = await stat9(sourceRoot).catch(() => null);
|
|
8247
8284
|
return info ? [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }] : [];
|
|
@@ -8255,29 +8292,25 @@ async function grokBotBackfillFiles(sourceRoot, home = os7.homedir(), env, optio
|
|
|
8255
8292
|
const info = await stat9(cachePath).catch(() => null);
|
|
8256
8293
|
return info ? [{ path: cachePath, modifiedAt: info.mtime.toISOString() }] : [];
|
|
8257
8294
|
}
|
|
8258
|
-
const
|
|
8259
|
-
const cloudOnly = [];
|
|
8295
|
+
const sessions = [];
|
|
8260
8296
|
for (const [conversationId, events] of map) {
|
|
8261
|
-
if (localIds.has(conversationId)) {
|
|
8262
|
-
continue;
|
|
8263
|
-
}
|
|
8264
8297
|
const session = cloudAgentSessionFromEvents(conversationId, events);
|
|
8265
8298
|
if (session) {
|
|
8266
|
-
|
|
8299
|
+
sessions.push(session);
|
|
8267
8300
|
}
|
|
8268
8301
|
}
|
|
8269
|
-
if (
|
|
8302
|
+
if (sessions.length === 0) {
|
|
8270
8303
|
return [];
|
|
8271
8304
|
}
|
|
8272
8305
|
return [{
|
|
8273
8306
|
path: cachePath,
|
|
8274
|
-
modifiedAt: await writeCursorCloudUsageCache(cachePath,
|
|
8307
|
+
modifiedAt: await writeCursorCloudUsageCache(cachePath, sessions)
|
|
8275
8308
|
}];
|
|
8276
8309
|
}
|
|
8277
8310
|
function createGrokBotAdapter() {
|
|
8278
8311
|
return {
|
|
8279
8312
|
id: SOURCE_ID2,
|
|
8280
|
-
label: "
|
|
8313
|
+
label: "Grok Bot",
|
|
8281
8314
|
agentName: AGENT_NAME2,
|
|
8282
8315
|
kind: "agent",
|
|
8283
8316
|
// No hooks to install — data arrives via the Cursor dashboard API.
|
|
@@ -11241,19 +11274,25 @@ async function parseModelNamesFromDynamicTexts(dynamicTextsPath) {
|
|
|
11241
11274
|
}
|
|
11242
11275
|
}
|
|
11243
11276
|
async function loadQoderCnModelNames(configDir2, home) {
|
|
11244
|
-
const map =
|
|
11245
|
-
const
|
|
11246
|
-
|
|
11247
|
-
const
|
|
11248
|
-
for (const [key, val] of Object.entries(siblingMap)) {
|
|
11277
|
+
const map = {};
|
|
11278
|
+
for (const appDirName of ["QoderCN", "Qoder"]) {
|
|
11279
|
+
const ideMap = await loadQoderIdeModelCatalog(appDirName, home);
|
|
11280
|
+
for (const [key, val] of Object.entries(ideMap)) {
|
|
11249
11281
|
if (!(key in map)) {
|
|
11250
11282
|
map[key] = val;
|
|
11251
11283
|
}
|
|
11252
11284
|
}
|
|
11253
11285
|
}
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
|
|
11286
|
+
const textsMap = await parseModelNamesFromDynamicTexts(path19.join(configDir2, ".auth", "dynamic-texts.json"));
|
|
11287
|
+
for (const [key, val] of Object.entries(textsMap)) {
|
|
11288
|
+
if (!(key in map)) {
|
|
11289
|
+
map[key] = val;
|
|
11290
|
+
}
|
|
11291
|
+
}
|
|
11292
|
+
const siblingConfigDir = configDir2.replace(/\.qoder-cn$/, ".qoder");
|
|
11293
|
+
if (siblingConfigDir !== configDir2) {
|
|
11294
|
+
const siblingMap = await parseModelNamesFromDynamicTexts(path19.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
|
|
11295
|
+
for (const [key, val] of Object.entries(siblingMap)) {
|
|
11257
11296
|
if (!(key in map)) {
|
|
11258
11297
|
map[key] = val;
|
|
11259
11298
|
}
|
|
@@ -12099,7 +12138,21 @@ async function parseModelNamesFromDynamicTexts2(dynamicTextsPath) {
|
|
|
12099
12138
|
}
|
|
12100
12139
|
}
|
|
12101
12140
|
async function loadQoderModelNames(configDir2, home) {
|
|
12102
|
-
const map =
|
|
12141
|
+
const map = {};
|
|
12142
|
+
for (const appDirName of ["Qoder", "QoderCN"]) {
|
|
12143
|
+
const ideMap = await loadQoderIdeModelCatalog(appDirName, home);
|
|
12144
|
+
for (const [key, val] of Object.entries(ideMap)) {
|
|
12145
|
+
if (!(key in map)) {
|
|
12146
|
+
map[key] = val;
|
|
12147
|
+
}
|
|
12148
|
+
}
|
|
12149
|
+
}
|
|
12150
|
+
const textsMap = await parseModelNamesFromDynamicTexts2(path20.join(configDir2, ".auth", "dynamic-texts.json"));
|
|
12151
|
+
for (const [key, val] of Object.entries(textsMap)) {
|
|
12152
|
+
if (!(key in map)) {
|
|
12153
|
+
map[key] = val;
|
|
12154
|
+
}
|
|
12155
|
+
}
|
|
12103
12156
|
const siblingConfigDir = configDir2.replace(/\.qoder$/, ".qoder-cn");
|
|
12104
12157
|
if (siblingConfigDir !== configDir2) {
|
|
12105
12158
|
const siblingMap = await parseModelNamesFromDynamicTexts2(path20.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
|
|
@@ -12122,14 +12175,6 @@ async function loadQoderModelNames(configDir2, home) {
|
|
|
12122
12175
|
}
|
|
12123
12176
|
}
|
|
12124
12177
|
}
|
|
12125
|
-
for (const appDirName of ["Qoder", "QoderCN"]) {
|
|
12126
|
-
const ideMap = await loadQoderIdeModelCatalog(appDirName, home);
|
|
12127
|
-
for (const [key, val] of Object.entries(ideMap)) {
|
|
12128
|
-
if (!(key in map)) {
|
|
12129
|
-
map[key] = val;
|
|
12130
|
-
}
|
|
12131
|
-
}
|
|
12132
|
-
}
|
|
12133
12178
|
return map;
|
|
12134
12179
|
}
|
|
12135
12180
|
function isQwenworkConfigRoot(configDir2) {
|
|
@@ -15190,8 +15235,8 @@ async function uninstallGeneratedFile(filePath, { dryRun, onWrite }) {
|
|
|
15190
15235
|
onWrite(`Would uninstall ${filePath}`);
|
|
15191
15236
|
return;
|
|
15192
15237
|
}
|
|
15193
|
-
const { unlink } = await import("node:fs/promises");
|
|
15194
|
-
await
|
|
15238
|
+
const { unlink: unlink2 } = await import("node:fs/promises");
|
|
15239
|
+
await unlink2(filePath);
|
|
15195
15240
|
onWrite(`Uninstalled ${filePath}`);
|
|
15196
15241
|
}
|
|
15197
15242
|
|
|
@@ -15258,7 +15303,7 @@ function defaultMachineName() {
|
|
|
15258
15303
|
init_fs();
|
|
15259
15304
|
|
|
15260
15305
|
// src/lib/logger.ts
|
|
15261
|
-
import { appendFile, mkdir as mkdir5, rename, stat as stat16 } from "node:fs/promises";
|
|
15306
|
+
import { appendFile, mkdir as mkdir5, rename as rename2, stat as stat16 } from "node:fs/promises";
|
|
15262
15307
|
import { homedir as homedir2 } from "node:os";
|
|
15263
15308
|
import path25 from "node:path";
|
|
15264
15309
|
var MAX_BYTES = 1 * 1024 * 1024;
|
|
@@ -15278,7 +15323,7 @@ async function rotateIfNeeded(file) {
|
|
|
15278
15323
|
try {
|
|
15279
15324
|
const info = await stat16(file);
|
|
15280
15325
|
if (info.size > MAX_BYTES) {
|
|
15281
|
-
await
|
|
15326
|
+
await rename2(file, `${file}.1`).catch(() => {
|
|
15282
15327
|
});
|
|
15283
15328
|
}
|
|
15284
15329
|
} catch {
|
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.70",
|
|
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": {
|