vibemon 1.10.0 → 1.10.1
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/README.md +6 -0
- package/package.json +1 -1
- package/stats.html +65 -9
package/README.md
CHANGED
|
@@ -25,6 +25,12 @@ The app launches in the system tray and listens on `http://127.0.0.1:19280`.
|
|
|
25
25
|
- **[Kiro](https://kiro.dev/)** - AWS's AI coding assistant
|
|
26
26
|
- **[OpenClaw](https://openclaw.ai/)** - Open-source computer use agent
|
|
27
27
|
|
|
28
|
+
## Integration Notes
|
|
29
|
+
|
|
30
|
+
- **Claude Code** and **Kiro** are the cleanest real-time integrations because they expose direct hook events around prompts, tool use, and turn completion.
|
|
31
|
+
- **Codex** is supported, but its interactive hook surface is currently narrower than Claude Code. For automation, `codex exec --json` provides richer telemetry.
|
|
32
|
+
- **OpenClaw** support is plugin-based. VibeMon uses a bridge plugin because OpenClaw's simpler internal hooks are not designed around the same tool loop.
|
|
33
|
+
|
|
28
34
|
## Features
|
|
29
35
|
|
|
30
36
|
- **Frameless Window** - Clean floating design
|
package/package.json
CHANGED
package/stats.html
CHANGED
|
@@ -306,11 +306,62 @@
|
|
|
306
306
|
return `${minutes}m`;
|
|
307
307
|
};
|
|
308
308
|
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
309
|
+
const titleCase = (value) => value
|
|
310
|
+
.split(/[\s_-]+/)
|
|
311
|
+
.filter(Boolean)
|
|
312
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
313
|
+
.join(' ');
|
|
314
|
+
|
|
315
|
+
const getNumericVersion = (parts, startIndex) => {
|
|
316
|
+
const versionParts = [];
|
|
317
|
+
|
|
318
|
+
for (let i = startIndex; i < parts.length; i++) {
|
|
319
|
+
const part = parts[i];
|
|
320
|
+
if (!/^\d+$/.test(part)) break;
|
|
321
|
+
// Ignore trailing release-date style suffixes like 20251101.
|
|
322
|
+
if (part.length >= 6) break;
|
|
323
|
+
versionParts.push(part);
|
|
324
|
+
if (versionParts.length === 2) break;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return versionParts.join('.');
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const getModelInfo = (model) => {
|
|
331
|
+
if (!model || typeof model !== 'string') {
|
|
332
|
+
return { key: 'unknown', name: 'Unknown' };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const parts = model.toLowerCase().split('-').filter(Boolean);
|
|
336
|
+
if (parts.length === 0) {
|
|
337
|
+
return { key: model, name: model };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const provider = parts[0];
|
|
341
|
+
const family = parts[1] || 'unknown';
|
|
342
|
+
const version = getNumericVersion(parts, 2);
|
|
343
|
+
const familyName = titleCase(family);
|
|
344
|
+
const name = version ? `${familyName} ${version}` : familyName;
|
|
345
|
+
const key = [provider, family, version].filter(Boolean).join(':');
|
|
346
|
+
|
|
347
|
+
return { key, name };
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const aggregateModelUsage = (modelUsage) => {
|
|
351
|
+
return Object.entries(modelUsage || {}).reduce((acc, [model, usage]) => {
|
|
352
|
+
const info = getModelInfo(model);
|
|
353
|
+
if (!acc[info.key]) {
|
|
354
|
+
acc[info.key] = { name: info.name };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
Object.entries(usage || {}).forEach(([metric, value]) => {
|
|
358
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
359
|
+
acc[info.key][metric] = (acc[info.key][metric] || 0) + value;
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
return acc;
|
|
364
|
+
}, {});
|
|
314
365
|
};
|
|
315
366
|
|
|
316
367
|
async function loadStats() {
|
|
@@ -502,13 +553,18 @@
|
|
|
502
553
|
|
|
503
554
|
function renderModelList(modelUsage) {
|
|
504
555
|
const container = document.getElementById('modelList');
|
|
505
|
-
const entries = Object.
|
|
506
|
-
|
|
507
|
-
|
|
556
|
+
const entries = Object.values(aggregateModelUsage(modelUsage))
|
|
557
|
+
.sort((a, b) => {
|
|
558
|
+
const totalA = (a.inputTokens || 0) + (a.outputTokens || 0);
|
|
559
|
+
const totalB = (b.inputTokens || 0) + (b.outputTokens || 0);
|
|
560
|
+
return totalB - totalA;
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
container.innerHTML = entries.map((usage) => {
|
|
508
564
|
const total = (usage.inputTokens || 0) + (usage.outputTokens || 0);
|
|
509
565
|
return `
|
|
510
566
|
<div class="model-item">
|
|
511
|
-
<span class="model-name">${
|
|
567
|
+
<span class="model-name">${usage.name}</span>
|
|
512
568
|
<span class="model-tokens">${formatNumber(total)} tokens</span>
|
|
513
569
|
</div>
|
|
514
570
|
`;
|