@yhong91/vibetime 0.1.64 → 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 +82 -16
- 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.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;
|
|
@@ -6334,10 +6334,18 @@ var CURSOR_DASHBOARD_USAGE_URL = "https://cursor.com/api/dashboard/get-filtered-
|
|
|
6334
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) {
|
|
@@ -6739,6 +6747,14 @@ async function loadCursorCloudUsageMap(options, filePath) {
|
|
|
6739
6747
|
if (cached) {
|
|
6740
6748
|
return cached;
|
|
6741
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
|
+
}
|
|
6742
6758
|
const pending = (async () => {
|
|
6743
6759
|
try {
|
|
6744
6760
|
const token = isCursorStateDbPath(filePath) ? await readCursorAccessToken(filePath) : await readCursorAccessTokenFromHome(options);
|
|
@@ -6746,7 +6762,10 @@ async function loadCursorCloudUsageMap(options, filePath) {
|
|
|
6746
6762
|
return /* @__PURE__ */ new Map();
|
|
6747
6763
|
}
|
|
6748
6764
|
const fetchImpl = typeof options.cursorCloudFetch === "function" ? options.cursorCloudFetch : void 0;
|
|
6749
|
-
|
|
6765
|
+
const map = await fetchCursorDashboardUsage({ accessToken: token, fetchImpl });
|
|
6766
|
+
await writeLastCloudFetchAt(home, (/* @__PURE__ */ new Date()).toISOString()).catch(() => {
|
|
6767
|
+
});
|
|
6768
|
+
return map;
|
|
6750
6769
|
} catch {
|
|
6751
6770
|
return /* @__PURE__ */ new Map();
|
|
6752
6771
|
}
|
|
@@ -6787,6 +6806,25 @@ async function resolveCursorSessionUsage(options, sessionId) {
|
|
|
6787
6806
|
function cursorCloudUsageCachePath(home) {
|
|
6788
6807
|
return path13.join(home, ".vibetime", CURSOR_CLOUD_USAGE_FILENAME);
|
|
6789
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
|
+
}
|
|
6790
6828
|
async function listLocalCursorSessionIds(home, env) {
|
|
6791
6829
|
const ids = /* @__PURE__ */ new Set();
|
|
6792
6830
|
for (const dbPath of cursorStateDbCandidates(home, env)) {
|
|
@@ -8040,7 +8078,12 @@ async function grokBotBackfillFiles(sourceRoot, home = os7.homedir(), env, optio
|
|
|
8040
8078
|
if (!options) {
|
|
8041
8079
|
return [];
|
|
8042
8080
|
}
|
|
8081
|
+
const cachePath = cursorCloudUsageCachePath(home);
|
|
8043
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
|
+
}
|
|
8044
8087
|
const localIds = await listLocalCursorSessionIds(home, env);
|
|
8045
8088
|
const cloudOnly = [];
|
|
8046
8089
|
for (const [conversationId, events] of map) {
|
|
@@ -8055,7 +8098,6 @@ async function grokBotBackfillFiles(sourceRoot, home = os7.homedir(), env, optio
|
|
|
8055
8098
|
if (cloudOnly.length === 0) {
|
|
8056
8099
|
return [];
|
|
8057
8100
|
}
|
|
8058
|
-
const cachePath = cursorCloudUsageCachePath(home);
|
|
8059
8101
|
return [{
|
|
8060
8102
|
path: cachePath,
|
|
8061
8103
|
modifiedAt: await writeCursorCloudUsageCache(cachePath, cloudOnly)
|
|
@@ -8799,6 +8841,36 @@ import path15 from "node:path";
|
|
|
8799
8841
|
|
|
8800
8842
|
// src/lib/toml-hooks.ts
|
|
8801
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
|
+
}
|
|
8802
8874
|
async function hasTomlHookCommand(filePath, command) {
|
|
8803
8875
|
const text = await readTextIfExists(filePath);
|
|
8804
8876
|
if (!text) {
|
|
@@ -8830,8 +8902,9 @@ function parseTomlHookRules(text) {
|
|
|
8830
8902
|
return rules;
|
|
8831
8903
|
}
|
|
8832
8904
|
function mergeTomlHookRules(existingText, desired) {
|
|
8905
|
+
const valid = desired.filter(isValidTomlHookRule);
|
|
8833
8906
|
const existingKeys = new Set(parseTomlHookRules(existingText).map(ruleKey));
|
|
8834
|
-
const toAppend =
|
|
8907
|
+
const toAppend = valid.filter((rule) => !existingKeys.has(ruleKey(rule)));
|
|
8835
8908
|
if (toAppend.length === 0) {
|
|
8836
8909
|
return existingText;
|
|
8837
8910
|
}
|
|
@@ -8962,7 +9035,6 @@ function kimiHookRules() {
|
|
|
8962
9035
|
"SessionStart",
|
|
8963
9036
|
"SessionEnd",
|
|
8964
9037
|
"UserPromptSubmit",
|
|
8965
|
-
"TurnStarted",
|
|
8966
9038
|
"PostToolUse",
|
|
8967
9039
|
"PostToolUseFailure",
|
|
8968
9040
|
"Stop",
|
|
@@ -14727,21 +14799,15 @@ async function mergeHooksToml(filePath, content, { dryRun, force, onWrite }) {
|
|
|
14727
14799
|
);
|
|
14728
14800
|
}
|
|
14729
14801
|
const desired = Array.isArray(content.hooks) ? content.hooks : [];
|
|
14730
|
-
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);
|
|
14731
14807
|
if (existingText === nextText || existingText === null && nextText === "") {
|
|
14732
14808
|
onWrite(`Already installed ${filePath}`);
|
|
14733
14809
|
return;
|
|
14734
14810
|
}
|
|
14735
|
-
if (existingText !== null) {
|
|
14736
|
-
const existingKeys = new Set(
|
|
14737
|
-
parseTomlHookRules(existingText).map((rule) => `${rule.event}\0${rule.command}`)
|
|
14738
|
-
);
|
|
14739
|
-
const allPresent = desired.every((rule) => existingKeys.has(`${rule.event}\0${rule.command}`));
|
|
14740
|
-
if (allPresent && desired.length > 0) {
|
|
14741
|
-
onWrite(`Already installed ${filePath}`);
|
|
14742
|
-
return;
|
|
14743
|
-
}
|
|
14744
|
-
}
|
|
14745
14811
|
await mkdir7(pathMod.dirname(filePath), { recursive: true });
|
|
14746
14812
|
await writeFile6(filePath, nextText, "utf8");
|
|
14747
14813
|
onWrite(`Installed ${filePath}`);
|
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": {
|