averecion-lite 1.7.0 → 1.8.0

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.
@@ -1390,6 +1390,59 @@ body {
1390
1390
  color: var(--text-primary);
1391
1391
  }
1392
1392
 
1393
+ /* Activity Filters */
1394
+ .activity-filters {
1395
+ display: flex;
1396
+ gap: 0.5rem;
1397
+ margin-bottom: 0.75rem;
1398
+ flex-wrap: wrap;
1399
+ }
1400
+
1401
+ .filter-btn {
1402
+ display: flex;
1403
+ align-items: center;
1404
+ gap: 0.4rem;
1405
+ background: var(--bg-card);
1406
+ border: 1px solid var(--border);
1407
+ color: var(--text-secondary);
1408
+ padding: 0.4rem 0.75rem;
1409
+ border-radius: 20px;
1410
+ font-size: 0.8rem;
1411
+ cursor: pointer;
1412
+ transition: all 0.2s;
1413
+ }
1414
+
1415
+ .filter-btn:hover {
1416
+ border-color: var(--text-secondary);
1417
+ color: var(--text-primary);
1418
+ }
1419
+
1420
+ .filter-btn.active {
1421
+ background: rgba(139, 92, 246, 0.15);
1422
+ border-color: var(--accent);
1423
+ color: var(--accent);
1424
+ }
1425
+
1426
+ .filter-dot {
1427
+ width: 8px;
1428
+ height: 8px;
1429
+ border-radius: 50%;
1430
+ display: inline-block;
1431
+ }
1432
+
1433
+ .filter-dot.safe { background: var(--success); }
1434
+ .filter-dot.flagged { background: var(--warning); }
1435
+ .filter-dot.attack { background: var(--danger); }
1436
+ .filter-dot.reviewed { background: var(--accent); }
1437
+
1438
+ .activity-filter-empty {
1439
+ color: var(--text-muted);
1440
+ font-size: 0.85rem;
1441
+ text-align: center;
1442
+ padding: 1.5rem 1rem;
1443
+ font-style: italic;
1444
+ }
1445
+
1393
1446
  @keyframes slideInLeft {
1394
1447
  from { opacity: 0; transform: translateX(-20px); }
1395
1448
  to { opacity: 1; transform: translateX(0); }
package/dashboard/dash.js CHANGED
@@ -245,6 +245,7 @@
245
245
  const eventId = event.id || `${event.ts}-${index}`;
246
246
  const showFeedback = event.decision === "blocked" || friendly.isAttack;
247
247
  const alreadyFeedback = feedback[eventId];
248
+ const category = friendly.isAttack ? "attack" : friendly.class === "blocked" ? "flagged" : friendly.class === "manual" ? "reviewed" : "safe";
248
249
 
249
250
  const feedbackHtml = showFeedback ? (alreadyFeedback ?
250
251
  `<div class="activity-feedback"><span class="feedback-thanks">Thanks for your feedback!</span></div>` :
@@ -257,7 +258,7 @@
257
258
 
258
259
  if (hasContext) {
259
260
  return `
260
- <div class="activity-item has-context" data-event-id="${eventId}">
261
+ <div class="activity-item has-context" data-category="${category}" data-event-id="${eventId}">
261
262
  <div class="activity-header">
262
263
  <div class="activity-icon ${friendly.class}">${friendly.icon}</div>
263
264
  <div class="activity-content">
@@ -274,7 +275,7 @@
274
275
  `;
275
276
  }
276
277
  return `
277
- <div class="activity-item" data-event-id="${eventId}">
278
+ <div class="activity-item" data-category="${category}" data-event-id="${eventId}">
278
279
  <div class="activity-icon ${friendly.class}">${friendly.icon}</div>
279
280
  <div class="activity-content">
280
281
  <div class="activity-text">${friendly.text}</div>
@@ -283,6 +284,7 @@
283
284
  </div>
284
285
  `;
285
286
  }).join("");
287
+ applyActivityFilter();
286
288
 
287
289
  // Attach feedback handlers for initial load
288
290
  activityList.querySelectorAll(".feedback-btn").forEach(btn => {
@@ -756,11 +758,13 @@
756
758
  let className = "approved";
757
759
  let text = `Tool executed: ${data.tool}`;
758
760
  let context = null;
761
+ let category = "safe";
759
762
 
760
763
  if (isBlocked || isDanger) {
761
764
  icon = "⚠️";
762
765
  className = "blocked";
763
766
  text = `Dangerous action: ${data.tool}`;
767
+ category = "flagged";
764
768
  context = {
765
769
  title: isDanger ? data.analysis.danger.reason : "Dangerous Action Detected",
766
770
  desc: "This action was flagged as potentially dangerous."
@@ -771,13 +775,13 @@
771
775
  icon = "🛡️";
772
776
  className = "blocked attack";
773
777
  text = `Caught attack in ${data.tool}`;
778
+ category = "attack";
774
779
  context = {
775
780
  title: "Prompt Injection Detected!",
776
781
  desc: data.analysis.injection.reason
777
782
  };
778
783
  }
779
784
 
780
- // Send browser notification for critical events
781
785
  if (isInjection) {
782
786
  showNotification("🛡️ Prompt Injection Detected!", `Attack attempt caught in ${data.tool}`, true);
783
787
  } else if (isDanger) {
@@ -795,7 +799,7 @@
795
799
  ` : "";
796
800
 
797
801
  const itemHtml = context ? `
798
- <div class="activity-item has-context" data-new="true" data-event-id="${eventId}">
802
+ <div class="activity-item has-context" data-new="true" data-category="${category}" data-event-id="${eventId}">
799
803
  <div class="activity-header">
800
804
  <div class="activity-icon ${className}">${icon}</div>
801
805
  <div class="activity-content">
@@ -810,7 +814,7 @@
810
814
  ${feedbackHtml}
811
815
  </div>
812
816
  ` : `
813
- <div class="activity-item" data-new="true" data-event-id="${eventId}">
817
+ <div class="activity-item" data-new="true" data-category="${category}" data-event-id="${eventId}">
814
818
  <div class="activity-icon ${className}">${icon}</div>
815
819
  <div class="activity-content">
816
820
  <div class="activity-text">${text}</div>
@@ -820,6 +824,7 @@
820
824
  `;
821
825
 
822
826
  activityList.insertAdjacentHTML("afterbegin", itemHtml);
827
+ applyActivityFilter();
823
828
 
824
829
  // Attach feedback button handlers
825
830
  const newItemEl = activityList.querySelector(`[data-event-id="${eventId}"]`);
@@ -1269,4 +1274,54 @@
1269
1274
  console.error("Failed to load archives:", err);
1270
1275
  }
1271
1276
  }
1277
+
1278
+ let activeFilter = "all";
1279
+
1280
+ function initActivityFilters() {
1281
+ const filterBar = document.getElementById("activity-filters");
1282
+ if (!filterBar) return;
1283
+
1284
+ filterBar.querySelectorAll(".filter-btn").forEach(btn => {
1285
+ btn.addEventListener("click", () => {
1286
+ filterBar.querySelectorAll(".filter-btn").forEach(b => b.classList.remove("active"));
1287
+ btn.classList.add("active");
1288
+ activeFilter = btn.dataset.filter;
1289
+ applyActivityFilter();
1290
+ });
1291
+ });
1292
+ }
1293
+
1294
+ function applyActivityFilter() {
1295
+ const activityList = document.getElementById("activity-list");
1296
+ if (!activityList) return;
1297
+
1298
+ const items = activityList.querySelectorAll(".activity-item");
1299
+ let visibleCount = 0;
1300
+
1301
+ items.forEach(item => {
1302
+ const cat = item.dataset.category;
1303
+ if (activeFilter === "all" || cat === activeFilter) {
1304
+ item.style.display = "";
1305
+ visibleCount++;
1306
+ } else {
1307
+ item.style.display = "none";
1308
+ }
1309
+ });
1310
+
1311
+ let emptyMsg = activityList.querySelector(".activity-filter-empty");
1312
+ if (visibleCount === 0 && items.length > 0) {
1313
+ if (!emptyMsg) {
1314
+ emptyMsg = document.createElement("div");
1315
+ emptyMsg.className = "activity-filter-empty";
1316
+ activityList.appendChild(emptyMsg);
1317
+ }
1318
+ const labels = { safe: "safe", flagged: "flagged", attack: "attack", reviewed: "reviewed" };
1319
+ emptyMsg.textContent = `No ${labels[activeFilter] || ""} activity to show`;
1320
+ emptyMsg.style.display = "";
1321
+ } else if (emptyMsg) {
1322
+ emptyMsg.style.display = "none";
1323
+ }
1324
+ }
1325
+
1326
+ initActivityFilters();
1272
1327
  })();
@@ -212,6 +212,21 @@
212
212
 
213
213
  <section class="activity-section">
214
214
  <h2>Recent Activity</h2>
215
+ <div class="activity-filters" id="activity-filters" data-testid="activity-filters">
216
+ <button class="filter-btn active" data-filter="all" data-testid="filter-all">All</button>
217
+ <button class="filter-btn" data-filter="safe" data-testid="filter-safe">
218
+ <span class="filter-dot safe"></span> Safe
219
+ </button>
220
+ <button class="filter-btn" data-filter="flagged" data-testid="filter-flagged">
221
+ <span class="filter-dot flagged"></span> Flagged
222
+ </button>
223
+ <button class="filter-btn" data-filter="attack" data-testid="filter-attack">
224
+ <span class="filter-dot attack"></span> Attacks
225
+ </button>
226
+ <button class="filter-btn" data-filter="reviewed" data-testid="filter-reviewed">
227
+ <span class="filter-dot reviewed"></span> Reviewed
228
+ </button>
229
+ </div>
215
230
  <div class="activity-list" id="activity-list" data-testid="list-activity">
216
231
  <div class="activity-empty">
217
232
  <p>🦞 Your bot hasn't done anything yet. When it does, you'll see it here!</p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "averecion-lite",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Real-time AI agent monitoring - watches logs, detects dangerous commands and prompt injection attempts",
5
5
  "author": "Averecion <hello@averecion.com>",
6
6
  "homepage": "https://github.com/averecion/clawguard#readme",