@yhong91/vibetime 0.1.45 → 0.1.47
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 +256 -14
- 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.47" : "0.1.1";
|
|
2051
2051
|
var DEFAULT_API_URL = "http://121.196.224.82:3001";
|
|
2052
2052
|
var DEFAULT_BACKFILL_BATCH_SIZE = 50;
|
|
2053
2053
|
var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
|
|
@@ -6222,6 +6222,7 @@ function createCopilotAdapter() {
|
|
|
6222
6222
|
import { readdir as readdir6, readFile as readFile8, stat as stat7 } from "node:fs/promises";
|
|
6223
6223
|
import path13 from "node:path";
|
|
6224
6224
|
init_fs();
|
|
6225
|
+
var GROK_COST_TICKS_PER_USD = 1e9;
|
|
6225
6226
|
var SOURCE_ID = "grok-build";
|
|
6226
6227
|
var AGENT_NAME = "grok-build";
|
|
6227
6228
|
var HOOK_COMMAND = `vibetime hook --agent ${SOURCE_ID}`;
|
|
@@ -6488,18 +6489,76 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6488
6489
|
}), lineNumber, type, "permission");
|
|
6489
6490
|
}
|
|
6490
6491
|
}
|
|
6492
|
+
let nextLine = lineNumber + 1;
|
|
6493
|
+
const turnUsages = await loadTurnUsagesFromUpdates(updatesPath);
|
|
6494
|
+
const hasTurnUsage = turnUsages.length > 0;
|
|
6495
|
+
for (const turn of turnUsages) {
|
|
6496
|
+
for (const entry of turn.entries) {
|
|
6497
|
+
push(base({
|
|
6498
|
+
ts: turn.ts,
|
|
6499
|
+
type: "model.usage",
|
|
6500
|
+
model: entry.model || model,
|
|
6501
|
+
confidence: "exact",
|
|
6502
|
+
metrics: entry.metrics,
|
|
6503
|
+
refs: stringRefs({
|
|
6504
|
+
sourceId: `${sessionId}:usage:${turn.promptId}:${entry.model || "model"}`,
|
|
6505
|
+
promptId: turn.promptId
|
|
6506
|
+
})
|
|
6507
|
+
}), nextLine, "updates", "model.usage");
|
|
6508
|
+
nextLine += 1;
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6511
|
+
const hunkPath = path13.join(sessionDir, "hunk_records.jsonl");
|
|
6512
|
+
const hunkActivities = await loadHunkFileActivities(hunkPath, cwd);
|
|
6513
|
+
if (hunkActivities.length > 0) {
|
|
6514
|
+
for (const event of events) {
|
|
6515
|
+
for (const file of event.fileActivities || []) {
|
|
6516
|
+
delete file.linesAdded;
|
|
6517
|
+
delete file.linesRemoved;
|
|
6518
|
+
}
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
let hasFileLineData = hunkActivities.length > 0;
|
|
6522
|
+
for (const activity of hunkActivities) {
|
|
6523
|
+
push(base({
|
|
6524
|
+
ts: activity.ts,
|
|
6525
|
+
type: "file.changed",
|
|
6526
|
+
confidence: "exact",
|
|
6527
|
+
fileActivities: [activity],
|
|
6528
|
+
metrics: {
|
|
6529
|
+
linesAdded: activity.linesAdded,
|
|
6530
|
+
linesRemoved: activity.linesRemoved,
|
|
6531
|
+
filesChanged: 1
|
|
6532
|
+
},
|
|
6533
|
+
refs: stringRefs({
|
|
6534
|
+
sourceId: `${sessionId}:hunk:${activity.fileActivityId || activity.path}`
|
|
6535
|
+
})
|
|
6536
|
+
}), nextLine, "hunk_records", "file.changed");
|
|
6537
|
+
nextLine += 1;
|
|
6538
|
+
}
|
|
6539
|
+
if (!hasFileLineData) {
|
|
6540
|
+
hasFileLineData = events.some(
|
|
6541
|
+
(event) => (event.fileActivities || []).some(
|
|
6542
|
+
(file) => (file.linesAdded || 0) > 0 || (file.linesRemoved || 0) > 0
|
|
6543
|
+
)
|
|
6544
|
+
);
|
|
6545
|
+
}
|
|
6491
6546
|
const signals = await readJsonIfExists(signalsPath);
|
|
6492
6547
|
if (isPlainObject(signals) && updatedAt) {
|
|
6493
|
-
const metrics = metricsFromSignals(signals
|
|
6548
|
+
const metrics = metricsFromSignals(signals, {
|
|
6549
|
+
includeLines: !hasFileLineData,
|
|
6550
|
+
includeModelCalls: !hasTurnUsage
|
|
6551
|
+
});
|
|
6494
6552
|
if (metrics) {
|
|
6495
6553
|
push(base({
|
|
6496
6554
|
ts: updatedAt,
|
|
6497
|
-
type: "model.usage",
|
|
6555
|
+
type: hasTurnUsage ? "agent.operation" : "model.usage",
|
|
6498
6556
|
model: stringField(signals, "primaryModelId") || model,
|
|
6499
6557
|
confidence: "partial",
|
|
6500
6558
|
metrics,
|
|
6501
|
-
refs: stringRefs({ sourceId: `${sessionId}:
|
|
6502
|
-
}),
|
|
6559
|
+
refs: stringRefs({ sourceId: `${sessionId}:signals` })
|
|
6560
|
+
}), nextLine, "signals", hasTurnUsage ? "agent.operation" : "model.usage");
|
|
6561
|
+
nextLine += 1;
|
|
6503
6562
|
}
|
|
6504
6563
|
}
|
|
6505
6564
|
if (updatedAt) {
|
|
@@ -6508,7 +6567,7 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6508
6567
|
type: "session.ended",
|
|
6509
6568
|
confidence: "derived",
|
|
6510
6569
|
refs: stringRefs({ sourceId: `${sessionId}:ended` })
|
|
6511
|
-
}),
|
|
6570
|
+
}), nextLine, "summary", "session.ended");
|
|
6512
6571
|
}
|
|
6513
6572
|
return events;
|
|
6514
6573
|
}
|
|
@@ -6566,16 +6625,36 @@ function fileActivitiesFromTool(toolName, input, ts, cwd) {
|
|
|
6566
6625
|
confidence: "exact"
|
|
6567
6626
|
}];
|
|
6568
6627
|
}
|
|
6569
|
-
if (lower === "search_replace" || lower
|
|
6628
|
+
if (lower === "search_replace" || lower.endsWith("__search_replace")) {
|
|
6570
6629
|
const target = stringField(input, "file_path") || stringField(input, "target_file") || stringField(input, "path");
|
|
6571
6630
|
if (!target) {
|
|
6572
6631
|
return [];
|
|
6573
6632
|
}
|
|
6633
|
+
const oldString = stringField(input, "old_string");
|
|
6634
|
+
const newString = stringField(input, "new_string");
|
|
6574
6635
|
return [{
|
|
6575
6636
|
ts,
|
|
6576
6637
|
path: displayPath(target, cwd),
|
|
6577
6638
|
operation: "edit",
|
|
6578
|
-
confidence: "exact"
|
|
6639
|
+
confidence: "exact",
|
|
6640
|
+
linesAdded: countTextLines(newString),
|
|
6641
|
+
linesRemoved: countTextLines(oldString),
|
|
6642
|
+
charsWritten: newString?.length
|
|
6643
|
+
}];
|
|
6644
|
+
}
|
|
6645
|
+
if (lower === "write" || lower.endsWith("__write")) {
|
|
6646
|
+
const target = stringField(input, "file_path") || stringField(input, "target_file") || stringField(input, "path");
|
|
6647
|
+
if (!target) {
|
|
6648
|
+
return [];
|
|
6649
|
+
}
|
|
6650
|
+
const content = stringField(input, "content");
|
|
6651
|
+
return [{
|
|
6652
|
+
ts,
|
|
6653
|
+
path: displayPath(target, cwd),
|
|
6654
|
+
operation: "write",
|
|
6655
|
+
confidence: "exact",
|
|
6656
|
+
linesAdded: countTextLines(content),
|
|
6657
|
+
charsWritten: content?.length
|
|
6579
6658
|
}];
|
|
6580
6659
|
}
|
|
6581
6660
|
return [];
|
|
@@ -6625,20 +6704,23 @@ async function loadToolInputsFromUpdates(updatesPath) {
|
|
|
6625
6704
|
}
|
|
6626
6705
|
return map;
|
|
6627
6706
|
}
|
|
6628
|
-
function metricsFromSignals(signals) {
|
|
6707
|
+
function metricsFromSignals(signals, options = {}) {
|
|
6708
|
+
const includeLines = options.includeLines !== false;
|
|
6709
|
+
const includeModelCalls = options.includeModelCalls !== false;
|
|
6629
6710
|
const toolCallCount = numberField(signals, "toolCallCount");
|
|
6630
6711
|
const turnCount = numberField(signals, "turnCount");
|
|
6631
6712
|
const durationSec = numberField(signals, "sessionDurationSeconds");
|
|
6632
|
-
const linesAdded = numberField(signals, "agentLinesAdded");
|
|
6633
|
-
const linesRemoved = numberField(signals, "agentLinesRemoved");
|
|
6634
|
-
|
|
6713
|
+
const linesAdded = includeLines ? numberField(signals, "agentLinesAdded") : void 0;
|
|
6714
|
+
const linesRemoved = includeLines ? numberField(signals, "agentLinesRemoved") : void 0;
|
|
6715
|
+
const filesTouched = includeLines ? numberField(signals, "agentFilesTouched") : void 0;
|
|
6716
|
+
if (toolCallCount === void 0 && (!includeModelCalls || turnCount === void 0) && durationSec === void 0 && linesAdded === void 0 && linesRemoved === void 0) {
|
|
6635
6717
|
return void 0;
|
|
6636
6718
|
}
|
|
6637
6719
|
const metrics = {};
|
|
6638
6720
|
if (toolCallCount !== void 0) {
|
|
6639
6721
|
metrics.toolCalls = toolCallCount;
|
|
6640
6722
|
}
|
|
6641
|
-
if (turnCount !== void 0) {
|
|
6723
|
+
if (includeModelCalls && turnCount !== void 0) {
|
|
6642
6724
|
metrics.modelCalls = turnCount;
|
|
6643
6725
|
}
|
|
6644
6726
|
if (durationSec !== void 0) {
|
|
@@ -6650,8 +6732,146 @@ function metricsFromSignals(signals) {
|
|
|
6650
6732
|
if (linesRemoved !== void 0) {
|
|
6651
6733
|
metrics.linesRemoved = linesRemoved;
|
|
6652
6734
|
}
|
|
6735
|
+
if (filesTouched !== void 0) {
|
|
6736
|
+
metrics.filesChanged = filesTouched;
|
|
6737
|
+
}
|
|
6738
|
+
return metrics;
|
|
6739
|
+
}
|
|
6740
|
+
async function loadTurnUsagesFromUpdates(updatesPath) {
|
|
6741
|
+
const turns = [];
|
|
6742
|
+
let text = "";
|
|
6743
|
+
try {
|
|
6744
|
+
text = await readFile8(updatesPath, "utf8");
|
|
6745
|
+
} catch {
|
|
6746
|
+
return turns;
|
|
6747
|
+
}
|
|
6748
|
+
for (const line of text.split("\n")) {
|
|
6749
|
+
if (!line.trim()) {
|
|
6750
|
+
continue;
|
|
6751
|
+
}
|
|
6752
|
+
const raw = parseJsonLine(line);
|
|
6753
|
+
if (!raw) {
|
|
6754
|
+
continue;
|
|
6755
|
+
}
|
|
6756
|
+
const params = objectField(raw, "params");
|
|
6757
|
+
const update = objectField(params, "update");
|
|
6758
|
+
if (stringField(update, "sessionUpdate") !== "turn_completed") {
|
|
6759
|
+
continue;
|
|
6760
|
+
}
|
|
6761
|
+
const usage = objectField(update, "usage");
|
|
6762
|
+
if (Object.keys(usage).length === 0) {
|
|
6763
|
+
continue;
|
|
6764
|
+
}
|
|
6765
|
+
const meta = objectField(params, "_meta");
|
|
6766
|
+
const ts = timestampFrom(meta.agentTimestampMs) || timestampFrom(raw.timestamp) || timestampFrom(raw.ts);
|
|
6767
|
+
if (!ts) {
|
|
6768
|
+
continue;
|
|
6769
|
+
}
|
|
6770
|
+
const promptId = stringField(update, "prompt_id") || stringField(meta, "eventId") || `turn_${turns.length + 1}`;
|
|
6771
|
+
const entries = [];
|
|
6772
|
+
const modelUsage = objectField(usage, "modelUsage");
|
|
6773
|
+
const modelKeys = Object.keys(modelUsage);
|
|
6774
|
+
if (modelKeys.length > 0) {
|
|
6775
|
+
for (const modelName of modelKeys) {
|
|
6776
|
+
const perModel = objectField(modelUsage, modelName);
|
|
6777
|
+
const metrics = metricsFromGrokUsage(
|
|
6778
|
+
Object.keys(perModel).length > 0 ? perModel : usage
|
|
6779
|
+
);
|
|
6780
|
+
if (!hasTokenMetrics(metrics)) {
|
|
6781
|
+
continue;
|
|
6782
|
+
}
|
|
6783
|
+
entries.push({ model: modelName, metrics });
|
|
6784
|
+
}
|
|
6785
|
+
} else {
|
|
6786
|
+
const metrics = metricsFromGrokUsage(usage);
|
|
6787
|
+
if (hasTokenMetrics(metrics)) {
|
|
6788
|
+
entries.push({ metrics });
|
|
6789
|
+
}
|
|
6790
|
+
}
|
|
6791
|
+
if (entries.length > 0) {
|
|
6792
|
+
turns.push({ ts, promptId, entries });
|
|
6793
|
+
}
|
|
6794
|
+
}
|
|
6795
|
+
return turns;
|
|
6796
|
+
}
|
|
6797
|
+
function metricsFromGrokUsage(usage) {
|
|
6798
|
+
const inputTokens = numberField(usage, "inputTokens") || 0;
|
|
6799
|
+
const outputTokens = numberField(usage, "outputTokens") || 0;
|
|
6800
|
+
const cachedRead = numberField(usage, "cachedReadTokens") || 0;
|
|
6801
|
+
const cacheCreation = numberField(usage, "cacheCreationTokens") || 0;
|
|
6802
|
+
const reasoning = numberField(usage, "reasoningTokens") || 0;
|
|
6803
|
+
const totalTokens = numberField(usage, "totalTokens") || inputTokens + outputTokens;
|
|
6804
|
+
const modelCalls = numberField(usage, "modelCalls");
|
|
6805
|
+
const apiDurationMs = numberField(usage, "apiDurationMs");
|
|
6806
|
+
const costTicks = numberField(usage, "costUsdTicks");
|
|
6807
|
+
const metrics = {
|
|
6808
|
+
tokensInput: inputTokens || void 0,
|
|
6809
|
+
tokensOutput: outputTokens || void 0,
|
|
6810
|
+
tokensCachedInput: cachedRead + cacheCreation || void 0,
|
|
6811
|
+
tokensCacheReadInput: cachedRead || void 0,
|
|
6812
|
+
tokensCacheCreationInput: cacheCreation || void 0,
|
|
6813
|
+
tokensReasoningOutput: reasoning || void 0,
|
|
6814
|
+
tokensTotal: totalTokens || void 0,
|
|
6815
|
+
modelCalls: modelCalls || void 0,
|
|
6816
|
+
modelDurationMs: apiDurationMs || void 0,
|
|
6817
|
+
durationMs: apiDurationMs || void 0
|
|
6818
|
+
};
|
|
6819
|
+
if (costTicks !== void 0 && costTicks > 0) {
|
|
6820
|
+
metrics.costUsd = costTicks / GROK_COST_TICKS_PER_USD;
|
|
6821
|
+
}
|
|
6653
6822
|
return metrics;
|
|
6654
6823
|
}
|
|
6824
|
+
function hasTokenMetrics(metrics) {
|
|
6825
|
+
return Boolean(
|
|
6826
|
+
metrics.tokensInput || metrics.tokensOutput || metrics.tokensCachedInput || metrics.tokensTotal
|
|
6827
|
+
);
|
|
6828
|
+
}
|
|
6829
|
+
async function loadHunkFileActivities(hunkPath, cwd) {
|
|
6830
|
+
const activities = [];
|
|
6831
|
+
let text = "";
|
|
6832
|
+
try {
|
|
6833
|
+
text = await readFile8(hunkPath, "utf8");
|
|
6834
|
+
} catch {
|
|
6835
|
+
return activities;
|
|
6836
|
+
}
|
|
6837
|
+
for (const line of text.split("\n")) {
|
|
6838
|
+
if (!line.trim()) {
|
|
6839
|
+
continue;
|
|
6840
|
+
}
|
|
6841
|
+
const raw = parseJsonLine(line);
|
|
6842
|
+
if (!raw) {
|
|
6843
|
+
continue;
|
|
6844
|
+
}
|
|
6845
|
+
const authorType = stringField(raw, "authorType");
|
|
6846
|
+
if (authorType && authorType !== "agent") {
|
|
6847
|
+
continue;
|
|
6848
|
+
}
|
|
6849
|
+
const filePath = stringField(raw, "filePath") || stringField(raw, "path");
|
|
6850
|
+
if (!filePath) {
|
|
6851
|
+
continue;
|
|
6852
|
+
}
|
|
6853
|
+
const ts = timestampFrom(raw.timestamp) || timestampFrom(raw.ts);
|
|
6854
|
+
if (!ts) {
|
|
6855
|
+
continue;
|
|
6856
|
+
}
|
|
6857
|
+
const linesAdded = numberField(raw, "linesAdded");
|
|
6858
|
+
const linesRemoved = numberField(raw, "linesRemoved");
|
|
6859
|
+
if ((linesAdded || 0) <= 0 && (linesRemoved || 0) <= 0) {
|
|
6860
|
+
continue;
|
|
6861
|
+
}
|
|
6862
|
+
const hunkId = stringField(raw, "hunkId");
|
|
6863
|
+
activities.push({
|
|
6864
|
+
ts,
|
|
6865
|
+
path: displayPath(filePath, cwd),
|
|
6866
|
+
operation: "edit",
|
|
6867
|
+
confidence: "exact",
|
|
6868
|
+
fileActivityId: hunkId,
|
|
6869
|
+
linesAdded: linesAdded || void 0,
|
|
6870
|
+
linesRemoved: linesRemoved || void 0
|
|
6871
|
+
});
|
|
6872
|
+
}
|
|
6873
|
+
return activities;
|
|
6874
|
+
}
|
|
6655
6875
|
var grokHandler = (msg) => hookHandler(SOURCE_ID, msg);
|
|
6656
6876
|
function hookConfig5() {
|
|
6657
6877
|
const anyTool = [{ matcher: ".*", hooks: [grokHandler("Reporting tool activity")] }];
|
|
@@ -6949,6 +7169,7 @@ async function parseOpenCodeSessionFile(dbPath, options) {
|
|
|
6949
7169
|
}
|
|
6950
7170
|
const filePath = stringField(stateInput, "file_path") || stringField(stateInput, "filePath");
|
|
6951
7171
|
if (filePath) {
|
|
7172
|
+
const lineStats = opencodeLineStats(tool, state, stateInput);
|
|
6952
7173
|
events.push(baseOpenCodeEvent({
|
|
6953
7174
|
ts: msToIso(endMs || completedTs || createdTs),
|
|
6954
7175
|
type: toolFileActivityType(tool),
|
|
@@ -6963,8 +7184,10 @@ async function parseOpenCodeSessionFile(dbPath, options) {
|
|
|
6963
7184
|
fileActivities: [{
|
|
6964
7185
|
ts: msToIso(endMs || completedTs || createdTs),
|
|
6965
7186
|
path: filePath,
|
|
6966
|
-
operation: operationForTool(tool)
|
|
7187
|
+
operation: operationForTool(tool),
|
|
7188
|
+
...lineStats
|
|
6967
7189
|
}],
|
|
7190
|
+
metrics: lineStats.linesAdded || lineStats.linesRemoved ? lineStats : void 0,
|
|
6968
7191
|
refs: stringRefs({ sourceId: callId })
|
|
6969
7192
|
}, assistantAgent));
|
|
6970
7193
|
}
|
|
@@ -7087,6 +7310,25 @@ function baseOpenCodeEvent(event, agentName) {
|
|
|
7087
7310
|
...event
|
|
7088
7311
|
};
|
|
7089
7312
|
}
|
|
7313
|
+
function opencodeLineStats(tool, state, input) {
|
|
7314
|
+
const diff = stringField(objectField(state, "metadata"), "diff");
|
|
7315
|
+
if (diff) {
|
|
7316
|
+
return diffStats(diff);
|
|
7317
|
+
}
|
|
7318
|
+
const normalized = tool.toLowerCase();
|
|
7319
|
+
if (normalized === "write") {
|
|
7320
|
+
return { linesAdded: countTextLines(stringField(input, "content")) };
|
|
7321
|
+
}
|
|
7322
|
+
if (normalized === "edit" || normalized === "multiedit" || normalized === "patch") {
|
|
7323
|
+
const newString = stringField(input, "newString") || stringField(input, "new_string");
|
|
7324
|
+
const oldString = stringField(input, "oldString") || stringField(input, "old_string");
|
|
7325
|
+
return {
|
|
7326
|
+
linesAdded: countTextLines(newString),
|
|
7327
|
+
linesRemoved: countTextLines(oldString)
|
|
7328
|
+
};
|
|
7329
|
+
}
|
|
7330
|
+
return {};
|
|
7331
|
+
}
|
|
7090
7332
|
function opencodeUsageFromInfo(info) {
|
|
7091
7333
|
const tokensObj = objectField(info, "tokens");
|
|
7092
7334
|
if (!tokensObj) {
|
package/package.json
CHANGED