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.
Files changed (3) hide show
  1. package/README.md +6 -0
  2. package/package.json +1 -1
  3. 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibemon",
3
- "version": "1.10.0",
3
+ "version": "1.10.1",
4
4
  "description": "AI assistant status monitor",
5
5
  "main": "main.js",
6
6
  "bin": {
package/stats.html CHANGED
@@ -306,11 +306,62 @@
306
306
  return `${minutes}m`;
307
307
  };
308
308
 
309
- const getModelShortName = (model) => {
310
- if (model.includes('opus')) return 'Opus 4.5';
311
- if (model.includes('sonnet')) return 'Sonnet 4.5';
312
- if (model.includes('haiku')) return 'Haiku';
313
- return model.split('-').slice(1, 3).join(' ');
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.entries(modelUsage);
506
-
507
- container.innerHTML = entries.map(([model, usage]) => {
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">${getModelShortName(model)}</span>
567
+ <span class="model-name">${usage.name}</span>
512
568
  <span class="model-tokens">${formatNumber(total)} tokens</span>
513
569
  </div>
514
570
  `;