@yhong91/vibetime 0.1.64 → 0.1.66
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 +227 -87
- 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.66" : "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;
|
|
@@ -5832,12 +5832,46 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5832
5832
|
let project;
|
|
5833
5833
|
let model;
|
|
5834
5834
|
let currentTurnId;
|
|
5835
|
+
let currentTurnStartedAt;
|
|
5836
|
+
let currentTurnLastEventAt;
|
|
5835
5837
|
let sessionStartedAt;
|
|
5836
5838
|
let reasoningEffort;
|
|
5839
|
+
let emittedShutdownUsage = false;
|
|
5840
|
+
let sawWorkSinceShutdown = false;
|
|
5841
|
+
const lastShutdownUsage = /* @__PURE__ */ new Map();
|
|
5837
5842
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
5838
5843
|
const turnTokenAccum = /* @__PURE__ */ new Map();
|
|
5839
|
-
const
|
|
5840
|
-
|
|
5844
|
+
const noteWork = (ts) => {
|
|
5845
|
+
if (currentTurnId) {
|
|
5846
|
+
currentTurnLastEventAt = ts;
|
|
5847
|
+
}
|
|
5848
|
+
sawWorkSinceShutdown = true;
|
|
5849
|
+
};
|
|
5850
|
+
const closeOpenTurn = () => {
|
|
5851
|
+
if (!currentTurnId || !currentTurnLastEventAt && !currentTurnStartedAt) {
|
|
5852
|
+
return;
|
|
5853
|
+
}
|
|
5854
|
+
const closedAt = currentTurnLastEventAt || currentTurnStartedAt;
|
|
5855
|
+
if (!closedAt) {
|
|
5856
|
+
return;
|
|
5857
|
+
}
|
|
5858
|
+
events.push(baseCopilotEvent({
|
|
5859
|
+
ts: closedAt,
|
|
5860
|
+
type: "turn.completed",
|
|
5861
|
+
sessionId,
|
|
5862
|
+
turnId: currentTurnId,
|
|
5863
|
+
cwd,
|
|
5864
|
+
project,
|
|
5865
|
+
model,
|
|
5866
|
+
confidence: "derived"
|
|
5867
|
+
}));
|
|
5868
|
+
};
|
|
5869
|
+
const releaseTurn = () => {
|
|
5870
|
+
closeOpenTurn();
|
|
5871
|
+
currentTurnId = void 0;
|
|
5872
|
+
currentTurnStartedAt = void 0;
|
|
5873
|
+
currentTurnLastEventAt = void 0;
|
|
5874
|
+
};
|
|
5841
5875
|
for (const [index, line] of lines.entries()) {
|
|
5842
5876
|
const lineNumber = index + 1;
|
|
5843
5877
|
const raw = parseJsonLine(line);
|
|
@@ -5875,7 +5909,11 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5875
5909
|
break;
|
|
5876
5910
|
}
|
|
5877
5911
|
case "user.message": {
|
|
5912
|
+
closeOpenTurn();
|
|
5878
5913
|
currentTurnId = `turn_${createStableHash([sessionId, lineNumber]).slice(0, 24)}`;
|
|
5914
|
+
currentTurnStartedAt = ts;
|
|
5915
|
+
currentTurnLastEventAt = ts;
|
|
5916
|
+
sawWorkSinceShutdown = true;
|
|
5879
5917
|
events.push(baseCopilotEvent({
|
|
5880
5918
|
ts,
|
|
5881
5919
|
type: "turn.started",
|
|
@@ -5913,8 +5951,8 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5913
5951
|
if (msgModel) {
|
|
5914
5952
|
model = msgModel;
|
|
5915
5953
|
}
|
|
5916
|
-
const
|
|
5917
|
-
|
|
5954
|
+
const turnId = currentTurnId;
|
|
5955
|
+
noteWork(ts);
|
|
5918
5956
|
events.push(baseCopilotEvent({
|
|
5919
5957
|
ts,
|
|
5920
5958
|
type: "agent.operation",
|
|
@@ -5928,20 +5966,20 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5928
5966
|
metrics: { modelCalls: 1 }
|
|
5929
5967
|
}));
|
|
5930
5968
|
const outputTokens = numberField(data, "outputTokens");
|
|
5931
|
-
if (outputTokens && outputTokens > 0) {
|
|
5932
|
-
const
|
|
5933
|
-
|
|
5934
|
-
accum.
|
|
5935
|
-
accum.
|
|
5936
|
-
turnTokenAccum.set(
|
|
5969
|
+
if (turnId && outputTokens && outputTokens > 0) {
|
|
5970
|
+
const accum = turnTokenAccum.get(turnId) || { metrics: { tokensOutput: 0, modelCalls: 0 }, ts };
|
|
5971
|
+
accum.metrics.tokensOutput = (accum.metrics.tokensOutput || 0) + outputTokens;
|
|
5972
|
+
accum.metrics.modelCalls = (accum.metrics.modelCalls || 0) + 1;
|
|
5973
|
+
accum.ts = ts;
|
|
5974
|
+
turnTokenAccum.set(turnId, accum);
|
|
5937
5975
|
}
|
|
5938
5976
|
break;
|
|
5939
5977
|
}
|
|
5940
5978
|
case "tool.execution_start": {
|
|
5941
5979
|
const toolCallId = stringField(data, "toolCallId");
|
|
5942
5980
|
const toolName = stringField(data, "toolName") || "tool";
|
|
5943
|
-
const
|
|
5944
|
-
|
|
5981
|
+
const turnId = currentTurnId;
|
|
5982
|
+
noteWork(ts);
|
|
5945
5983
|
if (toolCallId) {
|
|
5946
5984
|
pendingTools.set(toolCallId, { tool: toolName, startedAt: ts, turnId });
|
|
5947
5985
|
}
|
|
@@ -5985,6 +6023,7 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5985
6023
|
if (toolCallId) {
|
|
5986
6024
|
pendingTools.delete(toolCallId);
|
|
5987
6025
|
}
|
|
6026
|
+
noteWork(ts);
|
|
5988
6027
|
const success = data.success !== false;
|
|
5989
6028
|
const durationMs = pending ? Math.max(0, new Date(ts).getTime() - new Date(pending.startedAt).getTime()) : void 0;
|
|
5990
6029
|
events.push(baseCopilotEvent({
|
|
@@ -6027,54 +6066,27 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
6027
6066
|
break;
|
|
6028
6067
|
}
|
|
6029
6068
|
case "assistant.turn_end": {
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
accum.reasoningEffort = reasoningEffort;
|
|
6035
|
-
pendingTurnUsage.push(baseCopilotEvent({
|
|
6036
|
-
ts,
|
|
6037
|
-
type: "model.usage",
|
|
6038
|
-
sessionId,
|
|
6039
|
-
turnId,
|
|
6040
|
-
cwd,
|
|
6041
|
-
project,
|
|
6042
|
-
model,
|
|
6043
|
-
confidence: "partial",
|
|
6044
|
-
metrics: accum
|
|
6045
|
-
}));
|
|
6046
|
-
turnTokenAccum.delete(turnId);
|
|
6047
|
-
}
|
|
6048
|
-
events.push(baseCopilotEvent({
|
|
6049
|
-
ts,
|
|
6050
|
-
type: "turn.completed",
|
|
6051
|
-
sessionId,
|
|
6052
|
-
turnId,
|
|
6053
|
-
cwd,
|
|
6054
|
-
project,
|
|
6055
|
-
model,
|
|
6056
|
-
confidence: "derived"
|
|
6057
|
-
}));
|
|
6069
|
+
break;
|
|
6070
|
+
}
|
|
6071
|
+
case "session.resume": {
|
|
6072
|
+
releaseTurn();
|
|
6058
6073
|
break;
|
|
6059
6074
|
}
|
|
6060
6075
|
case "session.shutdown": {
|
|
6076
|
+
releaseTurn();
|
|
6061
6077
|
const modelMetrics2 = objectField(data, "modelMetrics");
|
|
6062
6078
|
for (const [modelName, metricsRaw] of Object.entries(modelMetrics2)) {
|
|
6063
6079
|
if (!isPlainObject(metricsRaw)) {
|
|
6064
6080
|
continue;
|
|
6065
6081
|
}
|
|
6066
|
-
const
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
const
|
|
6071
|
-
|
|
6072
|
-
const total = inputTokens + outputTokens;
|
|
6073
|
-
if (total <= 0) {
|
|
6082
|
+
const next = readShutdownSnapshot(metricsRaw);
|
|
6083
|
+
if (next.input + next.output <= 0) {
|
|
6084
|
+
continue;
|
|
6085
|
+
}
|
|
6086
|
+
const delta = positiveShutdownDelta(lastShutdownUsage.get(modelName), next);
|
|
6087
|
+
if (!delta) {
|
|
6074
6088
|
continue;
|
|
6075
6089
|
}
|
|
6076
|
-
const requests = objectField(metricsRaw, "requests");
|
|
6077
|
-
const requestCount = numberField(requests, "count");
|
|
6078
6090
|
events.push(baseCopilotEvent({
|
|
6079
6091
|
ts,
|
|
6080
6092
|
type: "model.usage",
|
|
@@ -6083,38 +6095,100 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
6083
6095
|
project,
|
|
6084
6096
|
model: modelName,
|
|
6085
6097
|
confidence: "exact",
|
|
6086
|
-
metrics:
|
|
6087
|
-
tokensInput: inputTokens || void 0,
|
|
6088
|
-
tokensOutput: outputTokens || void 0,
|
|
6089
|
-
tokensCachedInput: cacheReadTokens + cacheWriteTokens || void 0,
|
|
6090
|
-
tokensCacheReadInput: cacheReadTokens || void 0,
|
|
6091
|
-
tokensCacheCreationInput: cacheWriteTokens || void 0,
|
|
6092
|
-
tokensReasoningOutput: reasoningTokens || void 0,
|
|
6093
|
-
reasoningEffort,
|
|
6094
|
-
tokensTotal: total,
|
|
6095
|
-
modelCalls: requestCount || void 0
|
|
6096
|
-
}
|
|
6098
|
+
metrics: shutdownUsageMetrics(delta === "full" ? next : delta, reasoningEffort, delta === "full")
|
|
6097
6099
|
}));
|
|
6100
|
+
lastShutdownUsage.set(modelName, next);
|
|
6098
6101
|
emittedShutdownUsage = true;
|
|
6099
6102
|
}
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6103
|
+
if (sawWorkSinceShutdown) {
|
|
6104
|
+
events.push(baseCopilotEvent({
|
|
6105
|
+
ts,
|
|
6106
|
+
type: "session.ended",
|
|
6107
|
+
sessionId,
|
|
6108
|
+
cwd,
|
|
6109
|
+
project,
|
|
6110
|
+
model,
|
|
6111
|
+
operation: "session end"
|
|
6112
|
+
}));
|
|
6113
|
+
sawWorkSinceShutdown = false;
|
|
6114
|
+
}
|
|
6109
6115
|
break;
|
|
6110
6116
|
}
|
|
6111
6117
|
}
|
|
6112
6118
|
}
|
|
6119
|
+
if (isTurnIdle(currentTurnLastEventAt)) {
|
|
6120
|
+
closeOpenTurn();
|
|
6121
|
+
}
|
|
6113
6122
|
if (!emittedShutdownUsage) {
|
|
6114
|
-
|
|
6123
|
+
for (const [turnId, accum] of turnTokenAccum) {
|
|
6124
|
+
if (!accum.metrics.tokensOutput) {
|
|
6125
|
+
continue;
|
|
6126
|
+
}
|
|
6127
|
+
accum.metrics.reasoningEffort = reasoningEffort;
|
|
6128
|
+
events.push(baseCopilotEvent({
|
|
6129
|
+
ts: accum.ts,
|
|
6130
|
+
type: "model.usage",
|
|
6131
|
+
sessionId,
|
|
6132
|
+
turnId,
|
|
6133
|
+
cwd,
|
|
6134
|
+
project,
|
|
6135
|
+
model,
|
|
6136
|
+
confidence: "partial",
|
|
6137
|
+
metrics: accum.metrics
|
|
6138
|
+
}));
|
|
6139
|
+
}
|
|
6115
6140
|
}
|
|
6116
6141
|
return events.filter((event) => validateCanonicalEvent(event).valid);
|
|
6117
6142
|
}
|
|
6143
|
+
function readShutdownSnapshot(metricsRaw) {
|
|
6144
|
+
const usage = objectField(metricsRaw, "usage");
|
|
6145
|
+
const requests = objectField(metricsRaw, "requests");
|
|
6146
|
+
return {
|
|
6147
|
+
input: numberField(usage, "inputTokens") || 0,
|
|
6148
|
+
output: numberField(usage, "outputTokens") || 0,
|
|
6149
|
+
cacheRead: numberField(usage, "cacheReadTokens") || 0,
|
|
6150
|
+
cacheWrite: numberField(usage, "cacheWriteTokens") || 0,
|
|
6151
|
+
reasoning: numberField(usage, "reasoningTokens") || 0,
|
|
6152
|
+
requests: numberField(requests, "count") || 0
|
|
6153
|
+
};
|
|
6154
|
+
}
|
|
6155
|
+
function positiveShutdownDelta(prev, next) {
|
|
6156
|
+
if (!prev) {
|
|
6157
|
+
return "full";
|
|
6158
|
+
}
|
|
6159
|
+
const delta = {
|
|
6160
|
+
input: next.input - prev.input,
|
|
6161
|
+
output: next.output - prev.output,
|
|
6162
|
+
cacheRead: next.cacheRead - prev.cacheRead,
|
|
6163
|
+
cacheWrite: next.cacheWrite - prev.cacheWrite,
|
|
6164
|
+
reasoning: next.reasoning - prev.reasoning,
|
|
6165
|
+
requests: next.requests - prev.requests
|
|
6166
|
+
};
|
|
6167
|
+
if (delta.input <= 0 && delta.output <= 0 && delta.cacheRead <= 0 && delta.cacheWrite <= 0 && delta.requests <= 0) {
|
|
6168
|
+
return null;
|
|
6169
|
+
}
|
|
6170
|
+
return delta;
|
|
6171
|
+
}
|
|
6172
|
+
function shutdownUsageMetrics(snapshot, reasoningEffort, full) {
|
|
6173
|
+
const input = full ? snapshot.input : Math.max(0, snapshot.input);
|
|
6174
|
+
const output = full ? snapshot.output : Math.max(0, snapshot.output);
|
|
6175
|
+
const cacheRead = full ? snapshot.cacheRead : Math.max(0, snapshot.cacheRead);
|
|
6176
|
+
const cacheWrite = full ? snapshot.cacheWrite : Math.max(0, snapshot.cacheWrite);
|
|
6177
|
+
const reasoning = full ? snapshot.reasoning : Math.max(0, snapshot.reasoning);
|
|
6178
|
+
const requests = full ? snapshot.requests : Math.max(0, snapshot.requests);
|
|
6179
|
+
const total = input + output;
|
|
6180
|
+
return {
|
|
6181
|
+
tokensInput: input > 0 ? input : void 0,
|
|
6182
|
+
tokensOutput: output > 0 ? output : void 0,
|
|
6183
|
+
tokensCachedInput: cacheRead + cacheWrite > 0 ? cacheRead + cacheWrite : void 0,
|
|
6184
|
+
tokensCacheReadInput: cacheRead > 0 ? cacheRead : void 0,
|
|
6185
|
+
tokensCacheCreationInput: cacheWrite > 0 ? cacheWrite : void 0,
|
|
6186
|
+
tokensReasoningOutput: reasoning > 0 ? reasoning : void 0,
|
|
6187
|
+
reasoningEffort,
|
|
6188
|
+
tokensTotal: total > 0 ? total : void 0,
|
|
6189
|
+
modelCalls: requests > 0 ? requests : void 0
|
|
6190
|
+
};
|
|
6191
|
+
}
|
|
6118
6192
|
function copilotFileActivities(tool, args, ts, cwd) {
|
|
6119
6193
|
const normalized = tool.toLowerCase();
|
|
6120
6194
|
if (normalized === "apply_patch" && typeof args === "string") {
|
|
@@ -6334,10 +6408,18 @@ var CURSOR_DASHBOARD_USAGE_URL = "https://cursor.com/api/dashboard/get-filtered-
|
|
|
6334
6408
|
var CURSOR_CLOUD_USAGE_CACHE_VERSION = 2;
|
|
6335
6409
|
var DEFAULT_WINDOW_DAYS = 90;
|
|
6336
6410
|
var PAGE_SIZE = 1e3;
|
|
6411
|
+
var DEFAULT_CURSOR_CLOUD_FETCH_INTERVAL_SECONDS = 3600;
|
|
6337
6412
|
function cursorCloudUsageDisabled(env = process.env) {
|
|
6338
6413
|
const value = env.VIBETIME_CURSOR_CLOUD_USAGE;
|
|
6339
6414
|
return value === "0" || value === "false";
|
|
6340
6415
|
}
|
|
6416
|
+
function cursorCloudFetchIntervalSeconds(env = process.env) {
|
|
6417
|
+
const parsed = Number.parseInt(env.VIBETIME_CURSOR_CLOUD_FETCH_INTERVAL_SECONDS || "", 10);
|
|
6418
|
+
if (Number.isNaN(parsed) || parsed < 0) {
|
|
6419
|
+
return DEFAULT_CURSOR_CLOUD_FETCH_INTERVAL_SECONDS;
|
|
6420
|
+
}
|
|
6421
|
+
return parsed;
|
|
6422
|
+
}
|
|
6341
6423
|
function cursorSessionCookie(accessToken) {
|
|
6342
6424
|
const token = accessToken.trim();
|
|
6343
6425
|
if (!token) {
|
|
@@ -6739,6 +6821,14 @@ async function loadCursorCloudUsageMap(options, filePath) {
|
|
|
6739
6821
|
if (cached) {
|
|
6740
6822
|
return cached;
|
|
6741
6823
|
}
|
|
6824
|
+
const home = stringOption(options.home) || os6.homedir();
|
|
6825
|
+
const intervalSeconds = cursorCloudFetchIntervalSeconds();
|
|
6826
|
+
if (intervalSeconds > 0 && !options.force) {
|
|
6827
|
+
const lastFetchedMs = await readLastCloudFetchAt(home);
|
|
6828
|
+
if (lastFetchedMs !== void 0 && Date.now() - lastFetchedMs < intervalSeconds * 1e3) {
|
|
6829
|
+
return /* @__PURE__ */ new Map();
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6742
6832
|
const pending = (async () => {
|
|
6743
6833
|
try {
|
|
6744
6834
|
const token = isCursorStateDbPath(filePath) ? await readCursorAccessToken(filePath) : await readCursorAccessTokenFromHome(options);
|
|
@@ -6746,7 +6836,10 @@ async function loadCursorCloudUsageMap(options, filePath) {
|
|
|
6746
6836
|
return /* @__PURE__ */ new Map();
|
|
6747
6837
|
}
|
|
6748
6838
|
const fetchImpl = typeof options.cursorCloudFetch === "function" ? options.cursorCloudFetch : void 0;
|
|
6749
|
-
|
|
6839
|
+
const map = await fetchCursorDashboardUsage({ accessToken: token, fetchImpl });
|
|
6840
|
+
await writeLastCloudFetchAt(home, (/* @__PURE__ */ new Date()).toISOString()).catch(() => {
|
|
6841
|
+
});
|
|
6842
|
+
return map;
|
|
6750
6843
|
} catch {
|
|
6751
6844
|
return /* @__PURE__ */ new Map();
|
|
6752
6845
|
}
|
|
@@ -6787,6 +6880,25 @@ async function resolveCursorSessionUsage(options, sessionId) {
|
|
|
6787
6880
|
function cursorCloudUsageCachePath(home) {
|
|
6788
6881
|
return path13.join(home, ".vibetime", CURSOR_CLOUD_USAGE_FILENAME);
|
|
6789
6882
|
}
|
|
6883
|
+
function cursorCloudFetchStatePath(home) {
|
|
6884
|
+
return path13.join(home, ".vibetime", "cursor-cloud-usage-fetch.json");
|
|
6885
|
+
}
|
|
6886
|
+
async function readLastCloudFetchAt(home) {
|
|
6887
|
+
try {
|
|
6888
|
+
const raw = JSON.parse(await readFile8(cursorCloudFetchStatePath(home), "utf8"));
|
|
6889
|
+
const ts = isPlainObject(raw) ? stringField(raw, "fetchedAt") : void 0;
|
|
6890
|
+
const ms = ts ? Date.parse(ts) : Number.NaN;
|
|
6891
|
+
return Number.isNaN(ms) ? void 0 : ms;
|
|
6892
|
+
} catch {
|
|
6893
|
+
return void 0;
|
|
6894
|
+
}
|
|
6895
|
+
}
|
|
6896
|
+
async function writeLastCloudFetchAt(home, fetchedAt) {
|
|
6897
|
+
const statePath = cursorCloudFetchStatePath(home);
|
|
6898
|
+
await mkdir4(path13.dirname(statePath), { recursive: true });
|
|
6899
|
+
await writeFile4(statePath, `${JSON.stringify({ fetchedAt }, null, 2)}
|
|
6900
|
+
`, "utf8");
|
|
6901
|
+
}
|
|
6790
6902
|
async function listLocalCursorSessionIds(home, env) {
|
|
6791
6903
|
const ids = /* @__PURE__ */ new Set();
|
|
6792
6904
|
for (const dbPath of cursorStateDbCandidates(home, env)) {
|
|
@@ -8040,7 +8152,12 @@ async function grokBotBackfillFiles(sourceRoot, home = os7.homedir(), env, optio
|
|
|
8040
8152
|
if (!options) {
|
|
8041
8153
|
return [];
|
|
8042
8154
|
}
|
|
8155
|
+
const cachePath = cursorCloudUsageCachePath(home);
|
|
8043
8156
|
const map = await loadCursorCloudUsageMap(options);
|
|
8157
|
+
if (map.size === 0) {
|
|
8158
|
+
const info = await stat9(cachePath).catch(() => null);
|
|
8159
|
+
return info ? [{ path: cachePath, modifiedAt: info.mtime.toISOString() }] : [];
|
|
8160
|
+
}
|
|
8044
8161
|
const localIds = await listLocalCursorSessionIds(home, env);
|
|
8045
8162
|
const cloudOnly = [];
|
|
8046
8163
|
for (const [conversationId, events] of map) {
|
|
@@ -8055,7 +8172,6 @@ async function grokBotBackfillFiles(sourceRoot, home = os7.homedir(), env, optio
|
|
|
8055
8172
|
if (cloudOnly.length === 0) {
|
|
8056
8173
|
return [];
|
|
8057
8174
|
}
|
|
8058
|
-
const cachePath = cursorCloudUsageCachePath(home);
|
|
8059
8175
|
return [{
|
|
8060
8176
|
path: cachePath,
|
|
8061
8177
|
modifiedAt: await writeCursorCloudUsageCache(cachePath, cloudOnly)
|
|
@@ -8799,6 +8915,36 @@ import path15 from "node:path";
|
|
|
8799
8915
|
|
|
8800
8916
|
// src/lib/toml-hooks.ts
|
|
8801
8917
|
init_fs();
|
|
8918
|
+
var KIMI_CODE_HOOK_EVENTS = /* @__PURE__ */ new Set([
|
|
8919
|
+
"PreToolUse",
|
|
8920
|
+
"PostToolUse",
|
|
8921
|
+
"PostToolUseFailure",
|
|
8922
|
+
"PermissionRequest",
|
|
8923
|
+
"PermissionResult",
|
|
8924
|
+
"UserPromptSubmit",
|
|
8925
|
+
"Stop",
|
|
8926
|
+
"StopFailure",
|
|
8927
|
+
"Interrupt",
|
|
8928
|
+
"SessionStart",
|
|
8929
|
+
"SessionEnd",
|
|
8930
|
+
"SubagentStart",
|
|
8931
|
+
"SubagentStop",
|
|
8932
|
+
"PreCompact",
|
|
8933
|
+
"PostCompact",
|
|
8934
|
+
"Notification"
|
|
8935
|
+
]);
|
|
8936
|
+
function isValidTomlHookRule(rule) {
|
|
8937
|
+
if (!KIMI_CODE_HOOK_EVENTS.has(rule.event)) {
|
|
8938
|
+
return false;
|
|
8939
|
+
}
|
|
8940
|
+
if (typeof rule.command !== "string" || rule.command.trim() === "") {
|
|
8941
|
+
return false;
|
|
8942
|
+
}
|
|
8943
|
+
if (rule.timeout !== void 0 && (!Number.isInteger(rule.timeout) || rule.timeout < 1 || rule.timeout > 600)) {
|
|
8944
|
+
return false;
|
|
8945
|
+
}
|
|
8946
|
+
return true;
|
|
8947
|
+
}
|
|
8802
8948
|
async function hasTomlHookCommand(filePath, command) {
|
|
8803
8949
|
const text = await readTextIfExists(filePath);
|
|
8804
8950
|
if (!text) {
|
|
@@ -8830,8 +8976,9 @@ function parseTomlHookRules(text) {
|
|
|
8830
8976
|
return rules;
|
|
8831
8977
|
}
|
|
8832
8978
|
function mergeTomlHookRules(existingText, desired) {
|
|
8979
|
+
const valid = desired.filter(isValidTomlHookRule);
|
|
8833
8980
|
const existingKeys = new Set(parseTomlHookRules(existingText).map(ruleKey));
|
|
8834
|
-
const toAppend =
|
|
8981
|
+
const toAppend = valid.filter((rule) => !existingKeys.has(ruleKey(rule)));
|
|
8835
8982
|
if (toAppend.length === 0) {
|
|
8836
8983
|
return existingText;
|
|
8837
8984
|
}
|
|
@@ -8962,7 +9109,6 @@ function kimiHookRules() {
|
|
|
8962
9109
|
"SessionStart",
|
|
8963
9110
|
"SessionEnd",
|
|
8964
9111
|
"UserPromptSubmit",
|
|
8965
|
-
"TurnStarted",
|
|
8966
9112
|
"PostToolUse",
|
|
8967
9113
|
"PostToolUseFailure",
|
|
8968
9114
|
"Stop",
|
|
@@ -14727,21 +14873,15 @@ async function mergeHooksToml(filePath, content, { dryRun, force, onWrite }) {
|
|
|
14727
14873
|
);
|
|
14728
14874
|
}
|
|
14729
14875
|
const desired = Array.isArray(content.hooks) ? content.hooks : [];
|
|
14730
|
-
const
|
|
14876
|
+
const commands = [...new Set(
|
|
14877
|
+
desired.map((rule) => rule.command).filter((cmd) => typeof cmd === "string" && cmd.length > 0)
|
|
14878
|
+
)];
|
|
14879
|
+
const stripped = removeTomlHookRulesByCommand(existingText ?? "", commands);
|
|
14880
|
+
const nextText = mergeTomlHookRules(stripped, desired);
|
|
14731
14881
|
if (existingText === nextText || existingText === null && nextText === "") {
|
|
14732
14882
|
onWrite(`Already installed ${filePath}`);
|
|
14733
14883
|
return;
|
|
14734
14884
|
}
|
|
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
14885
|
await mkdir7(pathMod.dirname(filePath), { recursive: true });
|
|
14746
14886
|
await writeFile6(filePath, nextText, "utf8");
|
|
14747
14887
|
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.66",
|
|
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": {
|