web-agent-bridge 1.1.2 → 2.0.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.
- package/LICENSE +21 -21
- package/README.ar.md +446 -446
- package/README.md +780 -844
- package/bin/cli.js +80 -80
- package/bin/wab.js +80 -80
- package/examples/bidi-agent.js +119 -119
- package/examples/mcp-agent.js +94 -94
- package/examples/next-app-router/README.md +44 -0
- package/examples/puppeteer-agent.js +108 -108
- package/examples/saas-dashboard/README.md +55 -0
- package/examples/shopify-hydrogen/README.md +74 -0
- package/examples/vision-agent.js +171 -171
- package/examples/wordpress-elementor/README.md +77 -0
- package/package.json +69 -78
- package/public/.well-known/ai-assets.json +59 -0
- package/public/admin/login.html +84 -84
- package/public/ai.html +196 -0
- package/public/cookies.html +208 -208
- package/public/css/premium.css +317 -0
- package/public/css/styles.css +1235 -1235
- package/public/dashboard.html +704 -704
- package/public/demo.html +259 -0
- package/public/docs.html +585 -585
- package/public/feed.xml +89 -0
- package/public/index.html +495 -332
- package/public/js/auth-nav.js +31 -31
- package/public/js/auth-redirect.js +12 -12
- package/public/js/cookie-consent.js +56 -56
- package/public/js/wab-demo-page.js +721 -0
- package/public/js/ws-client.js +74 -74
- package/public/llms-full.txt +309 -0
- package/public/llms.txt +85 -0
- package/public/login.html +83 -83
- package/public/openapi.json +580 -0
- package/public/premium-dashboard.html +2487 -0
- package/public/premium.html +791 -0
- package/public/privacy.html +295 -295
- package/public/register.html +103 -103
- package/public/robots.txt +87 -0
- package/public/script/wab-consent.d.ts +36 -0
- package/public/script/wab-consent.js +104 -0
- package/public/script/wab-schema.js +131 -0
- package/public/script/wab.d.ts +108 -0
- package/public/script/wab.min.js +234 -0
- package/public/sitemap.xml +93 -0
- package/public/terms.html +254 -254
- package/public/video/tutorial.mp4 +0 -0
- package/script/ai-agent-bridge.js +1558 -1513
- package/sdk/README.md +55 -55
- package/sdk/index.d.ts +118 -0
- package/sdk/index.js +257 -203
- package/sdk/package.json +14 -14
- package/sdk/schema-discovery.js +83 -0
- package/server/config/secrets.js +94 -92
- package/server/index.js +0 -9
- package/server/middleware/adminAuth.js +30 -30
- package/server/middleware/auth.js +41 -41
- package/server/middleware/rateLimits.js +24 -24
- package/server/migrations/001_add_analytics_indexes.sql +7 -7
- package/server/migrations/002_premium_features.sql +418 -0
- package/server/models/adapters/index.js +33 -33
- package/server/models/adapters/mysql.js +183 -183
- package/server/models/adapters/postgresql.js +172 -172
- package/server/models/adapters/sqlite.js +7 -7
- package/server/models/db.js +561 -561
- package/server/routes/admin-premium.js +671 -0
- package/server/routes/admin.js +247 -247
- package/server/routes/api.js +131 -138
- package/server/routes/auth.js +51 -51
- package/server/routes/billing.js +45 -45
- package/server/routes/discovery.js +406 -329
- package/server/routes/license.js +240 -240
- package/server/routes/noscript.js +543 -543
- package/server/routes/premium-v2.js +686 -0
- package/server/routes/premium.js +724 -0
- package/server/routes/wab-api.js +476 -476
- package/server/services/agent-memory.js +625 -0
- package/server/services/email.js +204 -204
- package/server/services/fairness.js +420 -420
- package/server/services/plugins.js +747 -0
- package/server/services/premium.js +1883 -0
- package/server/services/self-healing.js +843 -0
- package/server/services/stripe.js +192 -192
- package/server/services/swarm.js +788 -0
- package/server/services/vision.js +871 -0
- package/server/utils/cache.js +125 -125
- package/server/utils/migrate.js +81 -81
- package/server/utils/secureFields.js +50 -50
- package/server/ws.js +101 -101
- package/docs/DEPLOY.md +0 -118
- package/docs/SPEC.md +0 -1540
- package/wab-mcp-adapter/README.md +0 -136
- package/wab-mcp-adapter/index.js +0 -555
- package/wab-mcp-adapter/package.json +0 -17
package/server/ws.js
CHANGED
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
const WebSocket = require('ws');
|
|
2
|
-
const { verifyUserToken, verifyAdminToken } = require('./config/secrets');
|
|
3
|
-
const { findSiteById } = require('./models/db');
|
|
4
|
-
|
|
5
|
-
// Map of siteId → Set of WebSocket clients
|
|
6
|
-
const siteClients = new Map();
|
|
7
|
-
|
|
8
|
-
function setupWebSocket(server) {
|
|
9
|
-
const wss = new WebSocket.Server({ server, path: '/ws/analytics' });
|
|
10
|
-
|
|
11
|
-
wss.on('connection', (ws, req) => {
|
|
12
|
-
let authenticatedSiteId = null;
|
|
13
|
-
|
|
14
|
-
ws.isAlive = true;
|
|
15
|
-
ws.on('pong', () => { ws.isAlive = true; });
|
|
16
|
-
|
|
17
|
-
ws.on('message', (data) => {
|
|
18
|
-
try {
|
|
19
|
-
const msg = JSON.parse(data);
|
|
20
|
-
|
|
21
|
-
if (msg.type === 'auth') {
|
|
22
|
-
if (!msg.token || !msg.siteId) {
|
|
23
|
-
ws.send(JSON.stringify({ type: 'error', message: 'token and siteId required' }));
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let decoded;
|
|
28
|
-
let isAdmin = false;
|
|
29
|
-
try {
|
|
30
|
-
decoded = verifyUserToken(msg.token);
|
|
31
|
-
} catch {
|
|
32
|
-
try {
|
|
33
|
-
decoded = verifyAdminToken(msg.token);
|
|
34
|
-
isAdmin = decoded.isAdmin === true;
|
|
35
|
-
} catch {
|
|
36
|
-
ws.send(JSON.stringify({ type: 'error', message: 'Invalid message or auth failed' }));
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (!isAdmin) {
|
|
42
|
-
const site = findSiteById.get(msg.siteId);
|
|
43
|
-
if (!site || site.user_id !== decoded.id) {
|
|
44
|
-
ws.send(JSON.stringify({ type: 'error', message: 'Forbidden: not your site' }));
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
authenticatedSiteId = msg.siteId;
|
|
50
|
-
if (!siteClients.has(msg.siteId)) {
|
|
51
|
-
siteClients.set(msg.siteId, new Set());
|
|
52
|
-
}
|
|
53
|
-
siteClients.get(msg.siteId).add(ws);
|
|
54
|
-
ws.send(JSON.stringify({ type: 'auth:success', siteId: msg.siteId }));
|
|
55
|
-
}
|
|
56
|
-
} catch (e) {
|
|
57
|
-
ws.send(JSON.stringify({ type: 'error', message: 'Invalid message or auth failed' }));
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
ws.on('close', () => {
|
|
62
|
-
if (authenticatedSiteId && siteClients.has(authenticatedSiteId)) {
|
|
63
|
-
siteClients.get(authenticatedSiteId).delete(ws);
|
|
64
|
-
if (siteClients.get(authenticatedSiteId).size === 0) {
|
|
65
|
-
siteClients.delete(authenticatedSiteId);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const interval = setInterval(() => {
|
|
72
|
-
wss.clients.forEach((ws) => {
|
|
73
|
-
if (!ws.isAlive) return ws.terminate();
|
|
74
|
-
ws.isAlive = false;
|
|
75
|
-
ws.ping();
|
|
76
|
-
});
|
|
77
|
-
}, 30000);
|
|
78
|
-
|
|
79
|
-
wss.on('close', () => clearInterval(interval));
|
|
80
|
-
|
|
81
|
-
return wss;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function broadcastAnalytic(siteId, eventData) {
|
|
85
|
-
const clients = siteClients.get(siteId);
|
|
86
|
-
if (!clients || clients.size === 0) return;
|
|
87
|
-
|
|
88
|
-
const message = JSON.stringify({
|
|
89
|
-
type: 'analytic',
|
|
90
|
-
timestamp: new Date().toISOString(),
|
|
91
|
-
...eventData
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
clients.forEach((ws) => {
|
|
95
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
96
|
-
ws.send(message);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
module.exports = { setupWebSocket, broadcastAnalytic };
|
|
1
|
+
const WebSocket = require('ws');
|
|
2
|
+
const { verifyUserToken, verifyAdminToken } = require('./config/secrets');
|
|
3
|
+
const { findSiteById } = require('./models/db');
|
|
4
|
+
|
|
5
|
+
// Map of siteId → Set of WebSocket clients
|
|
6
|
+
const siteClients = new Map();
|
|
7
|
+
|
|
8
|
+
function setupWebSocket(server) {
|
|
9
|
+
const wss = new WebSocket.Server({ server, path: '/ws/analytics' });
|
|
10
|
+
|
|
11
|
+
wss.on('connection', (ws, req) => {
|
|
12
|
+
let authenticatedSiteId = null;
|
|
13
|
+
|
|
14
|
+
ws.isAlive = true;
|
|
15
|
+
ws.on('pong', () => { ws.isAlive = true; });
|
|
16
|
+
|
|
17
|
+
ws.on('message', (data) => {
|
|
18
|
+
try {
|
|
19
|
+
const msg = JSON.parse(data);
|
|
20
|
+
|
|
21
|
+
if (msg.type === 'auth') {
|
|
22
|
+
if (!msg.token || !msg.siteId) {
|
|
23
|
+
ws.send(JSON.stringify({ type: 'error', message: 'token and siteId required' }));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let decoded;
|
|
28
|
+
let isAdmin = false;
|
|
29
|
+
try {
|
|
30
|
+
decoded = verifyUserToken(msg.token);
|
|
31
|
+
} catch {
|
|
32
|
+
try {
|
|
33
|
+
decoded = verifyAdminToken(msg.token);
|
|
34
|
+
isAdmin = decoded.isAdmin === true;
|
|
35
|
+
} catch {
|
|
36
|
+
ws.send(JSON.stringify({ type: 'error', message: 'Invalid message or auth failed' }));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!isAdmin) {
|
|
42
|
+
const site = findSiteById.get(msg.siteId);
|
|
43
|
+
if (!site || site.user_id !== decoded.id) {
|
|
44
|
+
ws.send(JSON.stringify({ type: 'error', message: 'Forbidden: not your site' }));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
authenticatedSiteId = msg.siteId;
|
|
50
|
+
if (!siteClients.has(msg.siteId)) {
|
|
51
|
+
siteClients.set(msg.siteId, new Set());
|
|
52
|
+
}
|
|
53
|
+
siteClients.get(msg.siteId).add(ws);
|
|
54
|
+
ws.send(JSON.stringify({ type: 'auth:success', siteId: msg.siteId }));
|
|
55
|
+
}
|
|
56
|
+
} catch (e) {
|
|
57
|
+
ws.send(JSON.stringify({ type: 'error', message: 'Invalid message or auth failed' }));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
ws.on('close', () => {
|
|
62
|
+
if (authenticatedSiteId && siteClients.has(authenticatedSiteId)) {
|
|
63
|
+
siteClients.get(authenticatedSiteId).delete(ws);
|
|
64
|
+
if (siteClients.get(authenticatedSiteId).size === 0) {
|
|
65
|
+
siteClients.delete(authenticatedSiteId);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const interval = setInterval(() => {
|
|
72
|
+
wss.clients.forEach((ws) => {
|
|
73
|
+
if (!ws.isAlive) return ws.terminate();
|
|
74
|
+
ws.isAlive = false;
|
|
75
|
+
ws.ping();
|
|
76
|
+
});
|
|
77
|
+
}, 30000);
|
|
78
|
+
|
|
79
|
+
wss.on('close', () => clearInterval(interval));
|
|
80
|
+
|
|
81
|
+
return wss;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function broadcastAnalytic(siteId, eventData) {
|
|
85
|
+
const clients = siteClients.get(siteId);
|
|
86
|
+
if (!clients || clients.size === 0) return;
|
|
87
|
+
|
|
88
|
+
const message = JSON.stringify({
|
|
89
|
+
type: 'analytic',
|
|
90
|
+
timestamp: new Date().toISOString(),
|
|
91
|
+
...eventData
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
clients.forEach((ws) => {
|
|
95
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
96
|
+
ws.send(message);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = { setupWebSocket, broadcastAnalytic };
|
package/docs/DEPLOY.md
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
# Deploying Web Agent Bridge to your server
|
|
2
|
-
|
|
3
|
-
The code is on GitHub (`master`). **You** deploy by SSH-ing into your VPS and pulling + restarting the app.
|
|
4
|
-
|
|
5
|
-
## Prerequisites
|
|
6
|
-
|
|
7
|
-
- Ubuntu/Debian (or similar) with Docker **or** Node.js 20+
|
|
8
|
-
- Firewall: open port **3000** (or your reverse proxy port 80/443)
|
|
9
|
-
- Strong `JWT_SECRET` and `JWT_SECRET_ADMIN` in production (never commit them)
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Option A — Docker Compose (recommended)
|
|
14
|
-
|
|
15
|
-
On the server, first time:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
cd /opt # or your preferred path
|
|
19
|
-
git clone https://github.com/abokenan444/web-agent-bridge.git
|
|
20
|
-
cd web-agent-bridge
|
|
21
|
-
cp .env.example .env # if present; else create .env with secrets and PORT
|
|
22
|
-
# Edit .env: JWT_SECRET=... JWT_SECRET_ADMIN=... PORT=3000
|
|
23
|
-
docker compose up -d --build
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Updates:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
cd /path/to/web-agent-bridge
|
|
30
|
-
git pull origin master
|
|
31
|
-
docker compose up -d --build
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Data persists in the Docker volume `wab-data` (SQLite under `/app/data` in the container).
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## Option B — Node.js (no Docker)
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
cd /path/to/web-agent-bridge
|
|
42
|
-
git pull origin master
|
|
43
|
-
npm ci --production
|
|
44
|
-
export NODE_ENV=production
|
|
45
|
-
export JWT_SECRET="your-long-random-secret"
|
|
46
|
-
export JWT_SECRET_ADMIN="your-different-long-random-secret"
|
|
47
|
-
export PORT=3000
|
|
48
|
-
node server/index.js
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Use **systemd**, **PM2**, or **screen/tmux** to keep the process running.
|
|
52
|
-
|
|
53
|
-
Example **PM2**:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
npm ci --production
|
|
57
|
-
pm2 start server/index.js --name wab
|
|
58
|
-
pm2 save
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
After `git pull`:
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
npm ci --production
|
|
65
|
-
pm2 restart wab
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## Reverse proxy (HTTPS)
|
|
71
|
-
|
|
72
|
-
Put **Nginx** or **Caddy** in front of `127.0.0.1:3000` and obtain TLS certificates (Let’s Encrypt).
|
|
73
|
-
|
|
74
|
-
Example Nginx location:
|
|
75
|
-
|
|
76
|
-
```nginx
|
|
77
|
-
location / {
|
|
78
|
-
proxy_pass http://127.0.0.1:3000;
|
|
79
|
-
proxy_http_version 1.1;
|
|
80
|
-
proxy_set_header Host $host;
|
|
81
|
-
proxy_set_header X-Real-IP $remote_addr;
|
|
82
|
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
83
|
-
proxy_set_header X-Forwarded-Proto $scheme;
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## WordPress plugin
|
|
90
|
-
|
|
91
|
-
Upload `web-agent-bridge-wordpress/` to the WordPress server (`wp-content/plugins/`) or sync via SFTP/rsync. Point **WAB API base URL** to your public URL (e.g. `https://wab.example.com`).
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## Why we do **not** recommend auto-deploy from this public repo
|
|
96
|
-
|
|
97
|
-
This project is **open source**. Wiring GitHub Actions (or similar) so that every push to `main`/`master` deploys to your production server is **high risk**:
|
|
98
|
-
|
|
99
|
-
| Risk | Why it matters |
|
|
100
|
-
|------|----------------|
|
|
101
|
-
| **Supply chain** | Anyone who can merge code (or bypass reviews) can change what runs on your server. |
|
|
102
|
-
| **Fork / PR abuse** | CI that builds untrusted PRs with secrets in scope has led to credential theft. |
|
|
103
|
-
| **Secret leakage** | Deploy keys and SSH keys in CI are juicy targets; misconfiguration exposes them in logs or artifacts. |
|
|
104
|
-
| **Public visibility** | Attackers see your pipeline definition and know exactly how prod is updated. |
|
|
105
|
-
|
|
106
|
-
**Recommended for operators:** deploy **manually** after you review changes (SSH → `git pull` → rebuild), or use a **private** deployment path (e.g. internal CI, private mirror, or release artifacts you promote by hand).
|
|
107
|
-
|
|
108
|
-
**If you must automate** (enterprise, private fork only): use a **private** repo or private runners, **branch protection**, required reviews, **deploy only from signed tags** or approved releases—not raw `main`—and never store production SSH keys in workflows reachable from public PR builds.
|
|
109
|
-
|
|
110
|
-
This repository intentionally does **not** ship a “deploy to production on push” workflow.
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## Checklist after deploy
|
|
115
|
-
|
|
116
|
-
- [ ] `curl -s https://your-domain/api/license/verify` returns JSON (POST with body) or app responds on `/`
|
|
117
|
-
- [ ] `https://your-domain/script/ai-agent-bridge.js` loads
|
|
118
|
-
- [ ] `.env` / `JWT_SECRET` / `JWT_SECRET_ADMIN` not exposed in git
|