web-agent-bridge 2.9.0 → 3.2.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 +51 -0
- package/README.ar.md +79 -0
- package/README.md +104 -4
- package/package.json +2 -1
- package/public/.well-known/ai-plugin.json +28 -0
- package/public/agent-workspace.html +3 -1
- package/public/ai.html +5 -3
- package/public/api.html +412 -0
- package/public/browser.html +4 -2
- package/public/cookies.html +4 -2
- package/public/dashboard.html +5 -3
- package/public/demo.html +1770 -1
- package/public/docs.html +6 -4
- package/public/growth.html +463 -0
- package/public/index.html +982 -738
- package/public/llms-full.txt +52 -1
- package/public/llms.txt +39 -0
- package/public/login.html +6 -4
- package/public/premium-dashboard.html +7 -5
- package/public/premium.html +6 -4
- package/public/privacy.html +4 -2
- package/public/register.html +6 -4
- package/public/score.html +263 -0
- package/public/terms.html +4 -2
- package/sdk/index.js +7 -1
- package/sdk/package.json +12 -1
- package/server/index.js +427 -375
- package/server/middleware/rateLimits.js +3 -3
- package/server/migrations/006_growth_suite.sql +138 -0
- package/server/routes/agent-workspace.js +162 -0
- package/server/routes/demo-showcase.js +332 -0
- package/server/routes/discovery.js +18 -7
- package/server/routes/gateway.js +157 -0
- package/server/routes/growth.js +962 -0
- package/server/routes/runtime.js +204 -0
- package/server/routes/universal.js +9 -1
- package/server/routes/wab-api.js +16 -6
- package/server/runtime/container-worker.js +111 -0
- package/server/runtime/container.js +448 -0
- package/server/runtime/distributed-worker.js +362 -0
- package/server/runtime/index.js +21 -1
- package/server/runtime/queue.js +599 -0
- package/server/runtime/replay.js +431 -29
- package/server/runtime/scheduler.js +194 -55
- package/server/services/api-key-engine.js +261 -0
- package/server/services/lfd.js +22 -3
- package/server/services/modules/affiliate-intelligence.js +93 -0
- package/server/services/modules/agent-firewall.js +90 -0
- package/server/services/modules/bounty.js +89 -0
- package/server/services/modules/collective-bargaining.js +92 -0
- package/server/services/modules/dark-pattern.js +66 -0
- package/server/services/modules/gov-intelligence.js +45 -0
- package/server/services/modules/neural.js +55 -0
- package/server/services/modules/notary.js +49 -0
- package/server/services/modules/price-time-machine.js +86 -0
- package/server/services/modules/protocol.js +104 -0
- package/server/services/premium.js +1 -1
- package/server/services/price-intelligence.js +2 -1
- package/server/services/vision.js +2 -2
package/server/index.js
CHANGED
|
@@ -1,375 +1,427 @@
|
|
|
1
|
-
require('dotenv').config();
|
|
2
|
-
|
|
3
|
-
const { assertSecretsAtStartup } = require('./config/secrets');
|
|
4
|
-
assertSecretsAtStartup();
|
|
5
|
-
|
|
6
|
-
const express = require('express');
|
|
7
|
-
const http = require('http');
|
|
8
|
-
const cors = require('cors');
|
|
9
|
-
const helmet = require('helmet');
|
|
10
|
-
const rateLimit = require('express-rate-limit');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
const { setupWebSocket } = require('./ws');
|
|
13
|
-
const { runMigrations } = require('./utils/migrate');
|
|
14
|
-
const { maybeBootstrapAdmin, db } = require('./models/db');
|
|
15
|
-
const { initSearchEngine, search, getSuggestions, getTrendingSearches, getSearchStats, purgeOldCache } = require('./services/search-engine');
|
|
16
|
-
const { processMessage: agentChat } = require('./services/agent-chat');
|
|
17
|
-
const agentTasks = require('./services/agent-tasks');
|
|
18
|
-
const { cluster } = require('./services/cluster');
|
|
19
|
-
|
|
20
|
-
const authRoutes = require('./routes/auth');
|
|
21
|
-
const apiRoutes = require('./routes/api');
|
|
22
|
-
const licenseRoutes = require('./routes/license');
|
|
23
|
-
const adminRoutes = require('./routes/admin');
|
|
24
|
-
const billingRoutes = require('./routes/billing');
|
|
25
|
-
const sovereignRoutes = require('./routes/sovereign');
|
|
26
|
-
const meshRoutes = require('./routes/mesh');
|
|
27
|
-
const commanderRoutes = require('./routes/commander');
|
|
28
|
-
const adsRoutes = require('./routes/ads');
|
|
29
|
-
const wabApiRoutes = require('./routes/wab-api');
|
|
30
|
-
const noscriptRoutes = require('./routes/noscript');
|
|
31
|
-
const discoveryRoutes = require('./routes/discovery');
|
|
32
|
-
const premiumRoutes = require('./routes/premium');
|
|
33
|
-
const adminPremiumRoutes = require('./routes/admin-premium');
|
|
34
|
-
const workspaceRoutes = require('./routes/agent-workspace');
|
|
35
|
-
const universalRoutes = require('./routes/universal');
|
|
36
|
-
const runtimeRoutes = require('./routes/runtime');
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return callback(null,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
app.use('
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
app.use('/
|
|
137
|
-
|
|
138
|
-
app.use('/api/
|
|
139
|
-
app.use('/api
|
|
140
|
-
app.use('/api/
|
|
141
|
-
app.use('/api/admin
|
|
142
|
-
app.use('/api/
|
|
143
|
-
app.use('/api/
|
|
144
|
-
app.use('/api/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
app.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
app.get('/
|
|
190
|
-
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
res.
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
res.
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
app.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
if (
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
1
|
+
require('dotenv').config();
|
|
2
|
+
|
|
3
|
+
const { assertSecretsAtStartup } = require('./config/secrets');
|
|
4
|
+
assertSecretsAtStartup();
|
|
5
|
+
|
|
6
|
+
const express = require('express');
|
|
7
|
+
const http = require('http');
|
|
8
|
+
const cors = require('cors');
|
|
9
|
+
const helmet = require('helmet');
|
|
10
|
+
const rateLimit = require('express-rate-limit');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { setupWebSocket } = require('./ws');
|
|
13
|
+
const { runMigrations } = require('./utils/migrate');
|
|
14
|
+
const { maybeBootstrapAdmin, db } = require('./models/db');
|
|
15
|
+
const { initSearchEngine, search, getSuggestions, getTrendingSearches, getSearchStats, purgeOldCache } = require('./services/search-engine');
|
|
16
|
+
const { processMessage: agentChat } = require('./services/agent-chat');
|
|
17
|
+
const agentTasks = require('./services/agent-tasks');
|
|
18
|
+
const { cluster } = require('./services/cluster');
|
|
19
|
+
|
|
20
|
+
const authRoutes = require('./routes/auth');
|
|
21
|
+
const apiRoutes = require('./routes/api');
|
|
22
|
+
const licenseRoutes = require('./routes/license');
|
|
23
|
+
const adminRoutes = require('./routes/admin');
|
|
24
|
+
const billingRoutes = require('./routes/billing');
|
|
25
|
+
const sovereignRoutes = require('./routes/sovereign');
|
|
26
|
+
const meshRoutes = require('./routes/mesh');
|
|
27
|
+
const commanderRoutes = require('./routes/commander');
|
|
28
|
+
const adsRoutes = require('./routes/ads');
|
|
29
|
+
const wabApiRoutes = require('./routes/wab-api');
|
|
30
|
+
const noscriptRoutes = require('./routes/noscript');
|
|
31
|
+
const discoveryRoutes = require('./routes/discovery');
|
|
32
|
+
const premiumRoutes = require('./routes/premium');
|
|
33
|
+
const adminPremiumRoutes = require('./routes/admin-premium');
|
|
34
|
+
const workspaceRoutes = require('./routes/agent-workspace');
|
|
35
|
+
const universalRoutes = require('./routes/universal');
|
|
36
|
+
const runtimeRoutes = require('./routes/runtime');
|
|
37
|
+
const demoShowcaseRoutes = require('./routes/demo-showcase');
|
|
38
|
+
const gatewayRoutes = require('./routes/gateway');
|
|
39
|
+
let growthRoutes;
|
|
40
|
+
try { growthRoutes = require('./routes/growth'); } catch { growthRoutes = require('express').Router(); }
|
|
41
|
+
const { handleWebhookRequest } = require('./services/stripe');
|
|
42
|
+
const { runtime } = require('./runtime');
|
|
43
|
+
|
|
44
|
+
const app = express();
|
|
45
|
+
const PORT = process.env.PORT || 3000;
|
|
46
|
+
|
|
47
|
+
app.set('trust proxy', 1);
|
|
48
|
+
|
|
49
|
+
const corsOrigins = (process.env.ALLOWED_ORIGINS
|
|
50
|
+
|| 'http://localhost:3000,http://127.0.0.1:3000,http://localhost:5173')
|
|
51
|
+
.split(',')
|
|
52
|
+
.map((s) => s.trim())
|
|
53
|
+
.filter(Boolean);
|
|
54
|
+
|
|
55
|
+
app.use(
|
|
56
|
+
cors({
|
|
57
|
+
origin(origin, callback) {
|
|
58
|
+
if (!origin) return callback(null, true);
|
|
59
|
+
if (corsOrigins.includes(origin)) return callback(null, true);
|
|
60
|
+
if (process.env.NODE_ENV !== 'production' && /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/i.test(origin)) {
|
|
61
|
+
return callback(null, true);
|
|
62
|
+
}
|
|
63
|
+
return callback(null, false);
|
|
64
|
+
},
|
|
65
|
+
credentials: true
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const scriptSrc = process.env.CSP_ALLOW_UNSAFE_INLINE === 'false'
|
|
70
|
+
? ["'self'"]
|
|
71
|
+
: ["'self'", "'unsafe-inline'"];
|
|
72
|
+
const styleSrc = process.env.CSP_ALLOW_UNSAFE_INLINE === 'false'
|
|
73
|
+
? ["'self'"]
|
|
74
|
+
: ["'self'", "'unsafe-inline'"];
|
|
75
|
+
|
|
76
|
+
app.use(
|
|
77
|
+
helmet({
|
|
78
|
+
contentSecurityPolicy: {
|
|
79
|
+
directives: {
|
|
80
|
+
defaultSrc: ["'self'"],
|
|
81
|
+
scriptSrc,
|
|
82
|
+
scriptSrcAttr: scriptSrc,
|
|
83
|
+
styleSrc: [...styleSrc, 'https://fonts.googleapis.com'],
|
|
84
|
+
imgSrc: ["'self'", 'data:', 'https:'],
|
|
85
|
+
connectSrc: ["'self'", 'ws:', 'wss:'],
|
|
86
|
+
fontSrc: ["'self'", 'https://fonts.gstatic.com', 'https:', 'data:'],
|
|
87
|
+
frameSrc: ["'self'", 'https:', 'http:'],
|
|
88
|
+
frameAncestors: ["'none'"],
|
|
89
|
+
objectSrc: ["'none'"],
|
|
90
|
+
baseUri: ["'self'"],
|
|
91
|
+
formAction: ["'self'"]
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
crossOriginEmbedderPolicy: false
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
app.post('/api/billing/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
99
|
+
try {
|
|
100
|
+
await handleWebhookRequest(req);
|
|
101
|
+
res.json({ received: true });
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error('Webhook error:', err.message);
|
|
104
|
+
res.status(400).json({ error: err.message });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
app.use(express.json());
|
|
109
|
+
|
|
110
|
+
const apiLimiter = rateLimit({
|
|
111
|
+
windowMs: 15 * 60 * 1000,
|
|
112
|
+
max: 200,
|
|
113
|
+
standardHeaders: true,
|
|
114
|
+
legacyHeaders: false,
|
|
115
|
+
message: { error: 'Too many requests, please try again later' }
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const licenseLimiter = rateLimit({
|
|
119
|
+
windowMs: 60 * 1000,
|
|
120
|
+
max: 120,
|
|
121
|
+
standardHeaders: true,
|
|
122
|
+
legacyHeaders: false,
|
|
123
|
+
keyGenerator: (req) => {
|
|
124
|
+
const key = req.body?.licenseKey || req.body?.siteId || req.ip;
|
|
125
|
+
return `${req.ip}:${key}`;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
app.use(express.static(path.join(__dirname, '..', 'public'), {
|
|
130
|
+
setHeaders(res, filePath) {
|
|
131
|
+
if (filePath.endsWith('.html')) {
|
|
132
|
+
res.setHeader('Cache-Control', 'no-cache, must-revalidate');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}));
|
|
136
|
+
app.use('/script', express.static(path.join(__dirname, '..', 'script')));
|
|
137
|
+
|
|
138
|
+
app.use('/api/auth', apiLimiter, authRoutes);
|
|
139
|
+
app.use('/api', apiLimiter, apiRoutes);
|
|
140
|
+
app.use('/api/license', licenseLimiter, licenseRoutes);
|
|
141
|
+
app.use('/api/admin', apiLimiter, adminRoutes);
|
|
142
|
+
app.use('/api/billing', apiLimiter, billingRoutes);
|
|
143
|
+
app.use('/api/sovereign', apiLimiter, sovereignRoutes);
|
|
144
|
+
app.use('/api/mesh', apiLimiter, meshRoutes);
|
|
145
|
+
app.use('/api/commander', apiLimiter, commanderRoutes);
|
|
146
|
+
app.use('/api/ads', apiLimiter, adsRoutes);
|
|
147
|
+
app.use('/api/wab', wabApiRoutes);
|
|
148
|
+
app.use('/api/noscript', apiLimiter, noscriptRoutes);
|
|
149
|
+
app.use('/api/discovery', apiLimiter, discoveryRoutes);
|
|
150
|
+
app.use('/api/premium', apiLimiter, premiumRoutes);
|
|
151
|
+
app.use('/api/admin/premium', apiLimiter, adminPremiumRoutes);
|
|
152
|
+
app.use('/api/workspace', apiLimiter, workspaceRoutes);
|
|
153
|
+
app.use('/api/universal', apiLimiter, universalRoutes);
|
|
154
|
+
app.use('/api/os', apiLimiter, runtimeRoutes);
|
|
155
|
+
app.use('/api/demo', apiLimiter, demoShowcaseRoutes);
|
|
156
|
+
app.use('/api/growth', apiLimiter, growthRoutes);
|
|
157
|
+
app.use('/api/v1', gatewayRoutes);
|
|
158
|
+
|
|
159
|
+
// Convenience alias: /api/negotiate/* → /api/sovereign/negotiation/*
|
|
160
|
+
app.get('/api/negotiate', apiLimiter, (req, res) => {
|
|
161
|
+
res.json({
|
|
162
|
+
engine: 'WAB Negotiation Engine',
|
|
163
|
+
endpoints: {
|
|
164
|
+
'POST /api/negotiate/rules': 'Create negotiation rules (auth required)',
|
|
165
|
+
'GET /api/negotiate/rules/:siteId': 'Get rules for a site',
|
|
166
|
+
'PUT /api/negotiate/rules/:ruleId': 'Update a rule (auth required)',
|
|
167
|
+
'POST /api/negotiate/sessions': 'Open negotiation session',
|
|
168
|
+
'POST /api/negotiate/sessions/:id/propose': 'Agent counter-offer',
|
|
169
|
+
'POST /api/negotiate/sessions/:id/confirm': 'Confirm deal',
|
|
170
|
+
'GET /api/negotiate/stats/:siteId': 'Negotiation stats',
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
app.use('/api/negotiate', apiLimiter, (req, res, next) => {
|
|
175
|
+
req.url = '/negotiation' + req.url;
|
|
176
|
+
sovereignRoutes(req, res, next);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// ─── WAB Search Engine ────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
const searchLimiter = rateLimit({
|
|
182
|
+
windowMs: 60 * 1000,
|
|
183
|
+
max: 30,
|
|
184
|
+
standardHeaders: true,
|
|
185
|
+
legacyHeaders: false,
|
|
186
|
+
message: { error: 'Too many search requests, please slow down' }
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
app.get('/api/search', searchLimiter, async (req, res) => {
|
|
190
|
+
const q = (req.query.q || '').trim();
|
|
191
|
+
if (!q) return res.json({ results: [], cached: false });
|
|
192
|
+
if (q.length > 200) return res.status(400).json({ error: 'Query too long' });
|
|
193
|
+
const crypto = require('crypto');
|
|
194
|
+
const ipHash = crypto.createHash('sha256').update(req.ip || '').digest('hex').slice(0, 16);
|
|
195
|
+
const result = await search(q, ipHash);
|
|
196
|
+
res.json(result);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
app.get('/api/search/suggest', searchLimiter, (req, res) => {
|
|
200
|
+
const q = (req.query.q || '').trim();
|
|
201
|
+
if (!q) return res.json({ suggestions: [] });
|
|
202
|
+
const suggestions = getSuggestions(q, 8);
|
|
203
|
+
res.json({ suggestions });
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
app.get('/api/search/trending', apiLimiter, (req, res) => {
|
|
207
|
+
const trending = getTrendingSearches(10);
|
|
208
|
+
res.json({ trending });
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
app.get('/api/search/stats', apiLimiter, (req, res) => {
|
|
212
|
+
const stats = getSearchStats();
|
|
213
|
+
res.json(stats);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Prevent browsers from caching HTML page routes
|
|
217
|
+
function noCache(req, res, next) {
|
|
218
|
+
res.set('Cache-Control', 'no-cache, must-revalidate');
|
|
219
|
+
next();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
app.get('/dashboard', noCache, (req, res) => {
|
|
223
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'dashboard.html'));
|
|
224
|
+
});
|
|
225
|
+
app.get('/mesh-dashboard', noCache, (req, res) => {
|
|
226
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'mesh-dashboard.html'));
|
|
227
|
+
});
|
|
228
|
+
app.get('/commander-dashboard', noCache, (req, res) => {
|
|
229
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'commander-dashboard.html'));
|
|
230
|
+
});
|
|
231
|
+
app.get('/docs', noCache, (req, res) => {
|
|
232
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'docs.html'));
|
|
233
|
+
});
|
|
234
|
+
app.get('/login', noCache, (req, res) => {
|
|
235
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'login.html'));
|
|
236
|
+
});
|
|
237
|
+
app.get('/register', noCache, (req, res) => {
|
|
238
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'register.html'));
|
|
239
|
+
});
|
|
240
|
+
app.get('/admin/login', noCache, (req, res) => {
|
|
241
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'admin', 'login.html'));
|
|
242
|
+
});
|
|
243
|
+
app.get('/admin', noCache, (req, res) => {
|
|
244
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'admin', 'dashboard.html'));
|
|
245
|
+
});
|
|
246
|
+
app.get('/privacy', noCache, (req, res) => {
|
|
247
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'privacy.html'));
|
|
248
|
+
});
|
|
249
|
+
app.get('/terms', noCache, (req, res) => {
|
|
250
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'terms.html'));
|
|
251
|
+
});
|
|
252
|
+
app.get('/cookies', noCache, (req, res) => {
|
|
253
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'cookies.html'));
|
|
254
|
+
});
|
|
255
|
+
app.get('/browser', noCache, (req, res) => {
|
|
256
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'browser.html'));
|
|
257
|
+
});
|
|
258
|
+
app.get('/workspace', noCache, (req, res) => {
|
|
259
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'agent-workspace.html'));
|
|
260
|
+
});
|
|
261
|
+
app.get('/growth', noCache, (req, res) => {
|
|
262
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'growth.html'));
|
|
263
|
+
});
|
|
264
|
+
app.get('/score', noCache, (req, res) => {
|
|
265
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'score.html'));
|
|
266
|
+
});
|
|
267
|
+
app.get('/api', noCache, (req, res) => {
|
|
268
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'api.html'));
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Browser downloads
|
|
272
|
+
app.use('/downloads', express.static(path.join(__dirname, '..', 'downloads'), {
|
|
273
|
+
maxAge: '1d',
|
|
274
|
+
setHeaders: (res, filePath) => {
|
|
275
|
+
res.set('Content-Disposition', 'attachment');
|
|
276
|
+
}
|
|
277
|
+
}));
|
|
278
|
+
|
|
279
|
+
// Agent chat endpoint for WAB Browser — Real AI Agent
|
|
280
|
+
const chatLimiter = rateLimit({
|
|
281
|
+
windowMs: 60 * 1000,
|
|
282
|
+
max: 20,
|
|
283
|
+
standardHeaders: true,
|
|
284
|
+
legacyHeaders: false,
|
|
285
|
+
message: { error: 'Too many messages, please slow down' }
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
app.post('/api/wab/agent-chat', chatLimiter, async (req, res) => {
|
|
289
|
+
const { message, context, sessionId, taskId, taskAction } = req.body || {};
|
|
290
|
+
if (!message || typeof message !== 'string') {
|
|
291
|
+
return res.status(400).json({ error: 'Message required' });
|
|
292
|
+
}
|
|
293
|
+
if (message.length > 3000) {
|
|
294
|
+
return res.status(400).json({ error: 'Message too long' });
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const sid = sessionId || req.ip || 'anonymous';
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
// ── Task actions (user responding to an active task) ──
|
|
301
|
+
if (taskId && taskAction) {
|
|
302
|
+
if (taskAction === 'answer') {
|
|
303
|
+
const result = agentTasks.answerClarification(taskId, message);
|
|
304
|
+
if (result.status === 'planning') {
|
|
305
|
+
// Auto-execute after planning
|
|
306
|
+
const execResult = await agentTasks.executeTask(taskId);
|
|
307
|
+
return res.json({ ...execResult, type: 'task' });
|
|
308
|
+
}
|
|
309
|
+
return res.json({ ...result, type: 'task' });
|
|
310
|
+
}
|
|
311
|
+
if (taskAction === 'select') {
|
|
312
|
+
const idx = parseInt(message.replace(/\D/g, '')) - 1;
|
|
313
|
+
const result = agentTasks.selectOffer(taskId, idx);
|
|
314
|
+
return res.json({ ...result, type: 'task' });
|
|
315
|
+
}
|
|
316
|
+
if (taskAction === 'cancel') {
|
|
317
|
+
const result = agentTasks.cancelTask(taskId);
|
|
318
|
+
return res.json({ ...result, type: 'task' });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// ── Check if user wants to select from existing offers ──
|
|
323
|
+
if (!taskId) {
|
|
324
|
+
const selectMatch = message.match(/(?:اختر|اخت(?:ا|ي)ر|select|choose|pick)\s*(\d+)/i);
|
|
325
|
+
if (selectMatch) {
|
|
326
|
+
const tasks = agentTasks.getSessionTasks(sid, 1);
|
|
327
|
+
if (tasks.length > 0 && tasks[0].status === 'presenting') {
|
|
328
|
+
const idx = parseInt(selectMatch[1]) - 1;
|
|
329
|
+
const result = agentTasks.selectOffer(tasks[0].id, idx);
|
|
330
|
+
return res.json({ ...result, type: 'task' });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ── Detect URL paste — create URL negotiation task ──
|
|
336
|
+
const urlData = agentTasks.parseBookingUrl(message);
|
|
337
|
+
if (urlData) {
|
|
338
|
+
const task = agentTasks.createUrlTask(sid, message, urlData);
|
|
339
|
+
const execResult = await agentTasks.executeUrlTask(task.taskId);
|
|
340
|
+
return res.json({ ...execResult, type: 'task', urlData });
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ── Detect if this is a task-type request (booking, shopping, etc.) ──
|
|
344
|
+
const intent = agentTasks.detectIntent(message);
|
|
345
|
+
if (intent.confidence >= 0.7 && intent.intent !== 'general') {
|
|
346
|
+
const task = agentTasks.createTask(sid, message);
|
|
347
|
+
|
|
348
|
+
if (task.status === 'clarifying') {
|
|
349
|
+
return res.json({ ...task, type: 'task' });
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// If requirements are complete, auto-execute
|
|
353
|
+
const execResult = await agentTasks.executeTask(task.taskId);
|
|
354
|
+
return res.json({ ...execResult, type: 'task' });
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ── Regular chat (not a task) ──
|
|
358
|
+
const chatContext = {
|
|
359
|
+
url: context?.url || '',
|
|
360
|
+
platform: context?.platform || 'unknown',
|
|
361
|
+
sessionId: sid,
|
|
362
|
+
};
|
|
363
|
+
const result = await agentChat(message, chatContext);
|
|
364
|
+
res.json(result);
|
|
365
|
+
} catch (err) {
|
|
366
|
+
console.error('[agent-chat] Error:', err.message);
|
|
367
|
+
res.json({ reply: '🤖 عذراً، حدث خطأ. حاول مرة أخرى.', type: 'text' });
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Agent task status & history
|
|
372
|
+
app.get('/api/wab/agent-task/:id', chatLimiter, (req, res) => {
|
|
373
|
+
const state = agentTasks.getTaskState(req.params.id);
|
|
374
|
+
if (!state) return res.status(404).json({ error: 'Task not found' });
|
|
375
|
+
res.json(state);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
app.get('/api/wab/agent-tasks', chatLimiter, (req, res) => {
|
|
379
|
+
const sid = req.query.sessionId || req.ip || 'anonymous';
|
|
380
|
+
const tasks = agentTasks.getSessionTasks(sid, 20);
|
|
381
|
+
res.json({ tasks });
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const pkg = require('../package.json');
|
|
385
|
+
app.use(`/v${pkg.version.split('.')[0]}`, express.static(path.join(__dirname, '..', 'script')));
|
|
386
|
+
app.use('/latest', express.static(path.join(__dirname, '..', 'script')));
|
|
387
|
+
|
|
388
|
+
app.get('*', (req, res) => {
|
|
389
|
+
// API routes always return JSON 404
|
|
390
|
+
if (req.path.startsWith('/api/')) {
|
|
391
|
+
return res.status(404).json({ error: 'Not found', path: req.path });
|
|
392
|
+
}
|
|
393
|
+
if (req.accepts('html')) {
|
|
394
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'index.html'));
|
|
395
|
+
} else {
|
|
396
|
+
res.status(404).json({ error: 'Not found' });
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
401
|
+
console.log('Running database migrations...');
|
|
402
|
+
runMigrations();
|
|
403
|
+
maybeBootstrapAdmin();
|
|
404
|
+
initSearchEngine(db);
|
|
405
|
+
|
|
406
|
+
// Purge old search cache every hour
|
|
407
|
+
setInterval(purgeOldCache, 60 * 60 * 1000);
|
|
408
|
+
|
|
409
|
+
const server = http.createServer(app);
|
|
410
|
+
setupWebSocket(server);
|
|
411
|
+
|
|
412
|
+
// Start Agent OS runtime
|
|
413
|
+
runtime.start();
|
|
414
|
+
|
|
415
|
+
// Start Cluster Orchestrator
|
|
416
|
+
cluster.start();
|
|
417
|
+
|
|
418
|
+
server.listen(PORT, () => {
|
|
419
|
+
console.log(`\n ╔══════════════════════════════════════════╗`);
|
|
420
|
+
console.log(` ║ Web Agent Bridge v${pkg.version} ║`);
|
|
421
|
+
console.log(` ║ Server running on http://localhost:${PORT} ║`);
|
|
422
|
+
console.log(` ║ WebSocket: ws://localhost:${PORT}/ws/analytics ║`);
|
|
423
|
+
console.log(` ╚══════════════════════════════════════════╝\n`);
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
module.exports = app;
|