web-agent-bridge 2.3.0 → 2.4.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.
Files changed (66) hide show
  1. package/README.ar.md +506 -31
  2. package/README.md +574 -47
  3. package/bin/agent-runner.js +10 -1
  4. package/package.json +12 -4
  5. package/public/agent-workspace.html +347 -0
  6. package/public/browser.html +484 -0
  7. package/public/commander-dashboard.html +243 -0
  8. package/public/css/agent-workspace.css +1713 -0
  9. package/public/css/premium.css +317 -317
  10. package/public/demo.html +259 -259
  11. package/public/index.html +738 -644
  12. package/public/js/agent-workspace.js +1740 -0
  13. package/public/mesh-dashboard.html +309 -382
  14. package/public/premium-dashboard.html +2487 -2487
  15. package/public/premium.html +791 -791
  16. package/public/script/wab.min.js +124 -87
  17. package/script/ai-agent-bridge.js +154 -84
  18. package/sdk/agent-mesh.js +287 -171
  19. package/sdk/commander.js +262 -0
  20. package/sdk/index.d.ts +83 -0
  21. package/sdk/index.js +374 -260
  22. package/sdk/package.json +1 -1
  23. package/server/config/secrets.js +13 -5
  24. package/server/index.js +191 -5
  25. package/server/middleware/adminAuth.js +6 -1
  26. package/server/middleware/auth.js +11 -2
  27. package/server/middleware/rateLimits.js +78 -2
  28. package/server/migrations/002_premium_features.sql +418 -418
  29. package/server/migrations/003_ads_integer_cents.sql +33 -0
  30. package/server/models/db.js +121 -1
  31. package/server/routes/admin-premium.js +671 -671
  32. package/server/routes/admin.js +16 -2
  33. package/server/routes/ads.js +130 -0
  34. package/server/routes/agent-workspace.js +378 -0
  35. package/server/routes/api.js +21 -2
  36. package/server/routes/auth.js +26 -6
  37. package/server/routes/commander.js +316 -0
  38. package/server/routes/mesh.js +370 -201
  39. package/server/routes/premium-v2.js +686 -686
  40. package/server/routes/premium.js +724 -724
  41. package/server/routes/sovereign.js +78 -0
  42. package/server/routes/universal.js +177 -0
  43. package/server/routes/wab-api.js +20 -5
  44. package/server/services/agent-chat.js +506 -0
  45. package/server/services/agent-learning.js +230 -77
  46. package/server/services/agent-memory.js +625 -625
  47. package/server/services/agent-mesh.js +260 -67
  48. package/server/services/agent-symphony.js +553 -517
  49. package/server/services/agent-tasks.js +1807 -0
  50. package/server/services/commander.js +738 -0
  51. package/server/services/edge-compute.js +440 -0
  52. package/server/services/fairness-engine.js +409 -0
  53. package/server/services/local-ai.js +389 -0
  54. package/server/services/plugins.js +771 -747
  55. package/server/services/price-intelligence.js +565 -0
  56. package/server/services/price-shield.js +1137 -0
  57. package/server/services/search-engine.js +357 -0
  58. package/server/services/security.js +513 -0
  59. package/server/services/self-healing.js +843 -843
  60. package/server/services/swarm.js +788 -788
  61. package/server/services/universal-scraper.js +661 -0
  62. package/server/services/vision.js +871 -871
  63. package/server/ws.js +61 -1
  64. package/public/admin/dashboard.html +0 -848
  65. package/public/admin/login.html +0 -84
  66. package/public/video/tutorial.mp4 +0 -0
package/server/ws.js CHANGED
@@ -1,20 +1,58 @@
1
1
  const WebSocket = require('ws');
2
2
  const { verifyUserToken, verifyAdminToken } = require('./config/secrets');
3
3
  const { findSiteById } = require('./models/db');
4
+ const { isJWTRevoked, auditLog } = require('./services/security');
4
5
 
5
6
  // Map of siteId → Set of WebSocket clients
6
7
  const siteClients = new Map();
8
+ // Per-IP connection tracking
9
+ const ipConnections = new Map();
10
+ const MAX_CONNECTIONS_PER_IP = 10;
11
+ const AUTH_TIMEOUT_MS = 10_000;
12
+ const MAX_MESSAGE_SIZE = 4096;
13
+ const MSG_RATE_WINDOW = 60_000;
14
+ const MSG_RATE_MAX = 30;
7
15
 
8
16
  function setupWebSocket(server) {
9
- const wss = new WebSocket.Server({ server, path: '/ws/analytics' });
17
+ const wss = new WebSocket.Server({ server, path: '/ws/analytics', maxPayload: MAX_MESSAGE_SIZE });
10
18
 
11
19
  wss.on('connection', (ws, req) => {
12
20
  let authenticatedSiteId = null;
21
+ const clientIP = req.headers['x-forwarded-for']?.split(',')[0]?.trim() || req.socket.remoteAddress || 'unknown';
22
+
23
+ // ── Per-IP connection limit ──
24
+ const currentCount = ipConnections.get(clientIP) || 0;
25
+ if (currentCount >= MAX_CONNECTIONS_PER_IP) {
26
+ ws.close(1013, 'Too many connections');
27
+ return;
28
+ }
29
+ ipConnections.set(clientIP, currentCount + 1);
30
+
31
+ // ── Auth timeout — close if not authenticated within 10s ──
32
+ const authTimer = setTimeout(() => {
33
+ if (!authenticatedSiteId) {
34
+ ws.close(4001, 'Authentication timeout');
35
+ }
36
+ }, AUTH_TIMEOUT_MS);
37
+
38
+ // ── Message rate limiter ──
39
+ const msgTimestamps = [];
13
40
 
14
41
  ws.isAlive = true;
15
42
  ws.on('pong', () => { ws.isAlive = true; });
16
43
 
17
44
  ws.on('message', (data) => {
45
+ // Rate limit messages
46
+ const now = Date.now();
47
+ msgTimestamps.push(now);
48
+ while (msgTimestamps.length > 0 && msgTimestamps[0] < now - MSG_RATE_WINDOW) {
49
+ msgTimestamps.shift();
50
+ }
51
+ if (msgTimestamps.length > MSG_RATE_MAX) {
52
+ ws.close(4008, 'Message rate limit exceeded');
53
+ return;
54
+ }
55
+
18
56
  try {
19
57
  const msg = JSON.parse(data);
20
58
 
@@ -24,6 +62,13 @@ function setupWebSocket(server) {
24
62
  return;
25
63
  }
26
64
 
65
+ // Check JWT revocation before verifying
66
+ if (isJWTRevoked(msg.token)) {
67
+ ws.send(JSON.stringify({ type: 'error', message: 'Token has been revoked' }));
68
+ ws.close(4003, 'Token revoked');
69
+ return;
70
+ }
71
+
27
72
  let decoded;
28
73
  let isAdmin = false;
29
74
  try {
@@ -34,6 +79,7 @@ function setupWebSocket(server) {
34
79
  isAdmin = decoded.isAdmin === true;
35
80
  } catch {
36
81
  ws.send(JSON.stringify({ type: 'error', message: 'Invalid message or auth failed' }));
82
+ auditLog({ actorType: 'system', action: 'ws_auth_failed', ip: clientIP, outcome: 'denied', severity: 'warning' });
37
83
  return;
38
84
  }
39
85
  }
@@ -46,12 +92,16 @@ function setupWebSocket(server) {
46
92
  }
47
93
  }
48
94
 
95
+ clearTimeout(authTimer);
49
96
  authenticatedSiteId = msg.siteId;
50
97
  if (!siteClients.has(msg.siteId)) {
51
98
  siteClients.set(msg.siteId, new Set());
52
99
  }
53
100
  siteClients.get(msg.siteId).add(ws);
54
101
  ws.send(JSON.stringify({ type: 'auth:success', siteId: msg.siteId }));
102
+ } else if (!authenticatedSiteId) {
103
+ // Reject all non-auth messages before authentication
104
+ ws.send(JSON.stringify({ type: 'error', message: 'Authentication required' }));
55
105
  }
56
106
  } catch (e) {
57
107
  ws.send(JSON.stringify({ type: 'error', message: 'Invalid message or auth failed' }));
@@ -59,6 +109,12 @@ function setupWebSocket(server) {
59
109
  });
60
110
 
61
111
  ws.on('close', () => {
112
+ clearTimeout(authTimer);
113
+ // Decrement IP connection count
114
+ const count = ipConnections.get(clientIP) || 1;
115
+ if (count <= 1) ipConnections.delete(clientIP);
116
+ else ipConnections.set(clientIP, count - 1);
117
+
62
118
  if (authenticatedSiteId && siteClients.has(authenticatedSiteId)) {
63
119
  siteClients.get(authenticatedSiteId).delete(ws);
64
120
  if (siteClients.get(authenticatedSiteId).size === 0) {
@@ -66,6 +122,10 @@ function setupWebSocket(server) {
66
122
  }
67
123
  }
68
124
  });
125
+
126
+ ws.on('error', () => {
127
+ clearTimeout(authTimer);
128
+ });
69
129
  });
70
130
 
71
131
  const interval = setInterval(() => {