vibeusage 0.2.19 → 0.2.20
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/package.json +1 -1
- package/src/commands/sync.js +125 -1
- package/src/lib/openclaw-hook.js +42 -4
package/package.json
CHANGED
package/src/commands/sync.js
CHANGED
|
@@ -123,6 +123,17 @@ async function cmdSync(argv) {
|
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
const openclawFallback = await applyOpenclawTotalsFallback({
|
|
127
|
+
trackerDir,
|
|
128
|
+
signal: openclawSignal,
|
|
129
|
+
cursors,
|
|
130
|
+
queuePath,
|
|
131
|
+
projectQueuePath
|
|
132
|
+
});
|
|
133
|
+
openclawResult.filesProcessed += openclawFallback.filesProcessed;
|
|
134
|
+
openclawResult.eventsAggregated += openclawFallback.eventsAggregated;
|
|
135
|
+
openclawResult.bucketsQueued += openclawFallback.bucketsQueued;
|
|
136
|
+
|
|
126
137
|
const claudeFiles = await listClaudeProjectFiles(claudeProjectsDir);
|
|
127
138
|
let claudeResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
128
139
|
if (claudeFiles.length > 0) {
|
|
@@ -432,10 +443,123 @@ function resolveOpenclawSignal({ home, env } = {}) {
|
|
|
432
443
|
const openclawHome = normalizeString(env.VIBEUSAGE_OPENCLAW_HOME) || path.join(home || os.homedir(), '.openclaw');
|
|
433
444
|
const sessionFile = path.join(openclawHome, 'agents', agentId, 'sessions', `${sessionId}.jsonl`);
|
|
434
445
|
|
|
435
|
-
|
|
446
|
+
const prevTotals = {
|
|
447
|
+
totalTokens: normalizeNonNegativeInt(env.VIBEUSAGE_OPENCLAW_PREV_TOTAL_TOKENS),
|
|
448
|
+
inputTokens: normalizeNonNegativeInt(env.VIBEUSAGE_OPENCLAW_PREV_INPUT_TOKENS),
|
|
449
|
+
outputTokens: normalizeNonNegativeInt(env.VIBEUSAGE_OPENCLAW_PREV_OUTPUT_TOKENS),
|
|
450
|
+
model: normalizeString(env.VIBEUSAGE_OPENCLAW_PREV_MODEL),
|
|
451
|
+
updatedAt: normalizeIsoOrEpoch(env.VIBEUSAGE_OPENCLAW_PREV_UPDATED_AT)
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
return {
|
|
455
|
+
agentId,
|
|
456
|
+
sessionId,
|
|
457
|
+
sessionKey: normalizeString(env.VIBEUSAGE_OPENCLAW_SESSION_KEY),
|
|
458
|
+
openclawHome,
|
|
459
|
+
sessionFile,
|
|
460
|
+
prevTotals
|
|
461
|
+
};
|
|
436
462
|
}
|
|
437
463
|
|
|
438
464
|
|
|
465
|
+
async function applyOpenclawTotalsFallback({ trackerDir, signal, cursors, queuePath, projectQueuePath }) {
|
|
466
|
+
const totalTokens = Number(signal?.prevTotals?.totalTokens || 0);
|
|
467
|
+
if (!trackerDir || !signal || totalTokens <= 0) {
|
|
468
|
+
return { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const sessionKey = `${signal.agentId}:${signal.sessionId}`;
|
|
472
|
+
const statePath = path.join(trackerDir, 'openclaw.fallback.state.json');
|
|
473
|
+
const fallbackFilePath = path.join(trackerDir, 'openclaw.fallback.jsonl');
|
|
474
|
+
const state = (await readJson(statePath)) || { version: 1, sessions: {} };
|
|
475
|
+
const sessions = state.sessions && typeof state.sessions === 'object' ? state.sessions : {};
|
|
476
|
+
const prev = sessions[sessionKey] && typeof sessions[sessionKey] === 'object' ? sessions[sessionKey] : null;
|
|
477
|
+
|
|
478
|
+
const current = {
|
|
479
|
+
totalTokens: normalizeNonNegativeInt(signal?.prevTotals?.totalTokens) || 0,
|
|
480
|
+
inputTokens: normalizeNonNegativeInt(signal?.prevTotals?.inputTokens) || 0,
|
|
481
|
+
outputTokens: normalizeNonNegativeInt(signal?.prevTotals?.outputTokens) || 0,
|
|
482
|
+
model: normalizeString(signal?.prevTotals?.model) || 'unknown',
|
|
483
|
+
updatedAt: normalizeIsoOrEpoch(signal?.prevTotals?.updatedAt) || new Date().toISOString(),
|
|
484
|
+
seenAt: new Date().toISOString()
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
let deltaTotal = current.totalTokens;
|
|
488
|
+
let deltaInput = current.inputTokens;
|
|
489
|
+
let deltaOutput = current.outputTokens;
|
|
490
|
+
if (prev) {
|
|
491
|
+
deltaTotal = Math.max(0, current.totalTokens - (normalizeNonNegativeInt(prev.totalTokens) || 0));
|
|
492
|
+
deltaInput = Math.max(0, current.inputTokens - (normalizeNonNegativeInt(prev.inputTokens) || 0));
|
|
493
|
+
deltaOutput = Math.max(0, current.outputTokens - (normalizeNonNegativeInt(prev.outputTokens) || 0));
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (deltaTotal > 0 && deltaInput + deltaOutput === 0) {
|
|
497
|
+
deltaInput = deltaTotal;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
sessions[sessionKey] = current;
|
|
501
|
+
state.version = 1;
|
|
502
|
+
state.sessions = sessions;
|
|
503
|
+
|
|
504
|
+
if (deltaTotal <= 0) {
|
|
505
|
+
await writeJson(statePath, state);
|
|
506
|
+
return { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
await ensureDir(path.dirname(fallbackFilePath));
|
|
510
|
+
const syntheticMessage = {
|
|
511
|
+
type: 'message',
|
|
512
|
+
timestamp: current.updatedAt,
|
|
513
|
+
message: {
|
|
514
|
+
role: 'assistant',
|
|
515
|
+
model: current.model,
|
|
516
|
+
usage: {
|
|
517
|
+
input: deltaInput,
|
|
518
|
+
output: deltaOutput,
|
|
519
|
+
cacheRead: 0,
|
|
520
|
+
cacheWrite: 0,
|
|
521
|
+
totalTokens: deltaTotal
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
await fs.appendFile(fallbackFilePath, `${JSON.stringify(syntheticMessage)}\n`, 'utf8');
|
|
526
|
+
await writeJson(statePath, state);
|
|
527
|
+
|
|
528
|
+
return parseOpenclawIncremental({
|
|
529
|
+
sessionFiles: [{ path: fallbackFilePath, source: 'openclaw' }],
|
|
530
|
+
cursors,
|
|
531
|
+
queuePath,
|
|
532
|
+
projectQueuePath,
|
|
533
|
+
source: 'openclaw'
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function normalizeNonNegativeInt(value) {
|
|
538
|
+
const n = Number(value);
|
|
539
|
+
if (!Number.isFinite(n) || n < 0) return null;
|
|
540
|
+
return Math.floor(n);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function normalizeIsoOrEpoch(value) {
|
|
544
|
+
if (typeof value === 'string') {
|
|
545
|
+
const trimmed = value.trim();
|
|
546
|
+
if (trimmed.length > 0 && !Number.isNaN(Date.parse(trimmed))) return trimmed;
|
|
547
|
+
const numeric = Number(trimmed);
|
|
548
|
+
if (Number.isFinite(numeric) && numeric > 0) {
|
|
549
|
+
const ms = numeric < 1e12 ? Math.floor(numeric * 1000) : Math.floor(numeric);
|
|
550
|
+
const iso = new Date(ms).toISOString();
|
|
551
|
+
if (!Number.isNaN(Date.parse(iso))) return iso;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const n = Number(value);
|
|
556
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
557
|
+
const ms = n < 1e12 ? Math.floor(n * 1000) : Math.floor(n);
|
|
558
|
+
const dt = new Date(ms);
|
|
559
|
+
if (Number.isNaN(dt.getTime())) return null;
|
|
560
|
+
return dt.toISOString();
|
|
561
|
+
}
|
|
562
|
+
|
|
439
563
|
async function safeStatSize(p) {
|
|
440
564
|
try {
|
|
441
565
|
const st = await fs.stat(p);
|
package/src/lib/openclaw-hook.js
CHANGED
|
@@ -252,12 +252,12 @@ function buildHookMarkdown() {
|
|
|
252
252
|
name: ${OPENCLAW_HOOK_NAME}
|
|
253
253
|
description: "Trigger vibeusage sync when OpenClaw sessions roll over"
|
|
254
254
|
metadata:
|
|
255
|
-
{ "openclaw": { "emoji": "📈", "events": ["command:new", "command:stop"], "requires": { "bins": ["node"] } } }
|
|
255
|
+
{ "openclaw": { "emoji": "📈", "events": ["command:new", "command:reset", "command:stop"], "requires": { "bins": ["node"] } } }
|
|
256
256
|
---
|
|
257
257
|
|
|
258
258
|
# VibeUsage OpenClaw Sync Hook
|
|
259
259
|
|
|
260
|
-
Triggers non-blocking 'vibeusage sync --auto --from-openclaw' runs when OpenClaw command events indicate
|
|
260
|
+
Triggers non-blocking 'vibeusage sync --auto --from-openclaw' runs when OpenClaw command events indicate session rollover/reset/stop.
|
|
261
261
|
`;
|
|
262
262
|
}
|
|
263
263
|
|
|
@@ -281,13 +281,14 @@ function buildHookHandler({ trackerDir, packageName = 'vibeusage', openclawHome
|
|
|
281
281
|
`module.exports = async function handler(event) {\n` +
|
|
282
282
|
` try {\n` +
|
|
283
283
|
` if (!event || event.type !== 'command') return;\n` +
|
|
284
|
-
` if (event.action !== 'new' && event.action !== 'stop') return;\n` +
|
|
284
|
+
` if (event.action !== 'new' && event.action !== 'reset' && event.action !== 'stop') return;\n` +
|
|
285
285
|
`\n` +
|
|
286
286
|
` const sessionKey = normalize(event.sessionKey);\n` +
|
|
287
287
|
` const agentId = parseAgentId(sessionKey);\n` +
|
|
288
288
|
` if (!agentId) return;\n` +
|
|
289
289
|
`\n` +
|
|
290
|
-
` const
|
|
290
|
+
` const sessionEntry = resolveSessionEntry(event);\n` +
|
|
291
|
+
` const sessionId = normalize(sessionEntry && sessionEntry.sessionId) || resolveSessionId(event);\n` +
|
|
291
292
|
` if (!sessionId) return;\n` +
|
|
292
293
|
`\n` +
|
|
293
294
|
` const now = Date.now();\n` +
|
|
@@ -302,9 +303,20 @@ function buildHookHandler({ trackerDir, packageName = 'vibeusage', openclawHome
|
|
|
302
303
|
` const env = {\n` +
|
|
303
304
|
` ...process.env,\n` +
|
|
304
305
|
` VIBEUSAGE_OPENCLAW_AGENT_ID: agentId,\n` +
|
|
306
|
+
` VIBEUSAGE_OPENCLAW_SESSION_KEY: sessionKey,\n` +
|
|
305
307
|
` VIBEUSAGE_OPENCLAW_PREV_SESSION_ID: sessionId,\n` +
|
|
306
308
|
` VIBEUSAGE_OPENCLAW_HOME: openclawHome\n` +
|
|
307
309
|
` };\n` +
|
|
310
|
+
` const prevTotalTokens = toNonNegativeInt(sessionEntry && sessionEntry.totalTokens);\n` +
|
|
311
|
+
` const prevInputTokens = toNonNegativeInt(sessionEntry && sessionEntry.inputTokens);\n` +
|
|
312
|
+
` const prevOutputTokens = toNonNegativeInt(sessionEntry && sessionEntry.outputTokens);\n` +
|
|
313
|
+
` const prevModel = normalize(sessionEntry && sessionEntry.model);\n` +
|
|
314
|
+
` const prevUpdatedAt = toIso(sessionEntry && sessionEntry.updatedAt);\n` +
|
|
315
|
+
` if (prevTotalTokens != null) env.VIBEUSAGE_OPENCLAW_PREV_TOTAL_TOKENS = String(prevTotalTokens);\n` +
|
|
316
|
+
` if (prevInputTokens != null) env.VIBEUSAGE_OPENCLAW_PREV_INPUT_TOKENS = String(prevInputTokens);\n` +
|
|
317
|
+
` if (prevOutputTokens != null) env.VIBEUSAGE_OPENCLAW_PREV_OUTPUT_TOKENS = String(prevOutputTokens);\n` +
|
|
318
|
+
` if (prevModel) env.VIBEUSAGE_OPENCLAW_PREV_MODEL = prevModel;\n` +
|
|
319
|
+
` if (prevUpdatedAt) env.VIBEUSAGE_OPENCLAW_PREV_UPDATED_AT = prevUpdatedAt;\n` +
|
|
308
320
|
`\n` +
|
|
309
321
|
` const hasLocalRuntime = fs.existsSync(trackerBinPath);\n` +
|
|
310
322
|
` const hasLocalDeps = fs.existsSync(depsMarkerPath);\n` +
|
|
@@ -323,6 +335,32 @@ function buildHookHandler({ trackerDir, packageName = 'vibeusage', openclawHome
|
|
|
323
335
|
` return s.length > 0 ? s : null;\n` +
|
|
324
336
|
`}\n` +
|
|
325
337
|
`\n` +
|
|
338
|
+
`function resolveSessionEntry(event) {\n` +
|
|
339
|
+
` const ctx = (event && event.context && typeof event.context === 'object') ? event.context : {};\n` +
|
|
340
|
+
` if (event && event.action === 'stop') return (ctx.sessionEntry && typeof ctx.sessionEntry === 'object') ? ctx.sessionEntry : null;\n` +
|
|
341
|
+
` if (ctx.previousSessionEntry && typeof ctx.previousSessionEntry === 'object') return ctx.previousSessionEntry;\n` +
|
|
342
|
+
` if (ctx.sessionEntry && typeof ctx.sessionEntry === 'object') return ctx.sessionEntry;\n` +
|
|
343
|
+
` return null;\n` +
|
|
344
|
+
`}\n` +
|
|
345
|
+
`\n` +
|
|
346
|
+
`function toNonNegativeInt(v) {\n` +
|
|
347
|
+
` const n = Number(v);\n` +
|
|
348
|
+
` if (!Number.isFinite(n) || n < 0) return null;\n` +
|
|
349
|
+
` return Math.floor(n);\n` +
|
|
350
|
+
`}\n` +
|
|
351
|
+
`\n` +
|
|
352
|
+
`function toIso(v) {\n` +
|
|
353
|
+
` if (typeof v === 'string') {\n` +
|
|
354
|
+
` const s = normalize(v);\n` +
|
|
355
|
+
` if (s && !Number.isNaN(Date.parse(s))) return s;\n` +
|
|
356
|
+
` }\n` +
|
|
357
|
+
` const n = Number(v);\n` +
|
|
358
|
+
` if (!Number.isFinite(n) || n <= 0) return null;\n` +
|
|
359
|
+
` const ms = n < 1e12 ? Math.floor(n * 1000) : Math.floor(n);\n` +
|
|
360
|
+
` const d = new Date(ms);\n` +
|
|
361
|
+
` return Number.isNaN(d.getTime()) ? null : d.toISOString();\n` +
|
|
362
|
+
`}\n` +
|
|
363
|
+
`\n` +
|
|
326
364
|
`function parseAgentId(sessionKey) {\n` +
|
|
327
365
|
` const s = normalize(sessionKey);\n` +
|
|
328
366
|
` if (!s || !s.startsWith('agent:')) return null;\n` +
|