web-agent-bridge 3.0.0 → 3.3.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 +72 -21
- package/README.ar.md +1286 -1073
- package/README.md +1764 -1535
- package/bin/agent-runner.js +474 -474
- package/bin/cli.js +237 -138
- package/bin/wab.js +80 -80
- package/examples/bidi-agent.js +119 -119
- package/examples/cross-site-agent.js +91 -91
- package/examples/mcp-agent.js +94 -94
- package/examples/next-app-router/README.md +44 -44
- package/examples/puppeteer-agent.js +108 -108
- package/examples/saas-dashboard/README.md +55 -55
- package/examples/shopify-hydrogen/README.md +74 -74
- package/examples/vision-agent.js +171 -171
- package/examples/wordpress-elementor/README.md +77 -77
- package/package.json +17 -3
- package/public/.well-known/agent-tools.json +180 -180
- package/public/.well-known/ai-assets.json +59 -59
- package/public/.well-known/ai-plugin.json +28 -0
- package/public/.well-known/security.txt +8 -0
- package/public/agent-workspace.html +349 -347
- package/public/ai.html +198 -196
- package/public/api.html +413 -0
- package/public/browser.html +486 -484
- package/public/commander-dashboard.html +243 -243
- package/public/cookies.html +210 -208
- package/public/css/agent-workspace.css +1713 -1713
- package/public/css/premium.css +317 -317
- package/public/css/styles.css +1235 -1235
- package/public/dashboard.html +706 -704
- package/public/demo.html +1770 -1
- package/public/dns.html +507 -0
- package/public/docs.html +587 -585
- package/public/feed.xml +89 -89
- package/public/growth.html +463 -0
- package/public/index.html +341 -9
- package/public/integrations.html +556 -0
- package/public/js/agent-workspace.js +1740 -1740
- 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 -721
- package/public/js/ws-client.js +74 -74
- package/public/llms-full.txt +360 -309
- package/public/llms.txt +125 -86
- package/public/login.html +85 -83
- package/public/mesh-dashboard.html +328 -328
- package/public/openapi.json +580 -580
- package/public/phone-shield.html +281 -0
- package/public/premium-dashboard.html +2489 -2487
- package/public/premium.html +793 -791
- package/public/privacy.html +297 -295
- package/public/register.html +105 -103
- package/public/robots.txt +87 -87
- package/public/script/wab-consent.d.ts +36 -36
- package/public/script/wab-consent.js +104 -104
- package/public/script/wab-schema.js +131 -131
- package/public/script/wab.d.ts +108 -108
- package/public/script/wab.min.js +580 -580
- package/public/security.txt +8 -0
- package/public/terms.html +256 -254
- package/script/ai-agent-bridge.js +1754 -1754
- package/sdk/README.md +99 -99
- package/sdk/agent-mesh.js +449 -449
- package/sdk/commander.js +262 -262
- package/sdk/index.d.ts +464 -464
- package/sdk/index.js +18 -1
- package/sdk/multi-agent.js +318 -318
- package/sdk/package.json +12 -1
- package/sdk/safety-shield.js +219 -0
- package/sdk/schema-discovery.js +83 -83
- package/server/adapters/index.js +520 -520
- package/server/config/plans.js +367 -367
- package/server/config/secrets.js +102 -102
- package/server/control-plane/index.js +301 -301
- package/server/data-plane/index.js +354 -354
- package/server/index.js +175 -19
- package/server/llm/index.js +404 -404
- package/server/middleware/adminAuth.js +35 -35
- package/server/middleware/auth.js +50 -50
- package/server/middleware/featureGate.js +88 -88
- package/server/middleware/rateLimits.js +100 -100
- package/server/middleware/sensitiveAction.js +157 -0
- package/server/migrations/001_add_analytics_indexes.sql +7 -7
- package/server/migrations/002_premium_features.sql +418 -418
- package/server/migrations/003_ads_integer_cents.sql +33 -33
- package/server/migrations/004_agent_os.sql +158 -158
- package/server/migrations/005_marketplace_metering.sql +126 -126
- 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 +681 -681
- package/server/observability/failure-analysis.js +337 -337
- package/server/observability/index.js +394 -394
- package/server/protocol/capabilities.js +223 -223
- package/server/protocol/index.js +243 -243
- package/server/protocol/schema.js +584 -584
- package/server/registry/certification.js +271 -271
- package/server/registry/index.js +326 -326
- package/server/routes/admin-premium.js +671 -671
- package/server/routes/admin.js +261 -261
- package/server/routes/ads.js +130 -130
- package/server/routes/agent-workspace.js +540 -378
- package/server/routes/api.js +150 -150
- package/server/routes/auth.js +71 -71
- package/server/routes/billing.js +45 -45
- package/server/routes/commander.js +316 -316
- package/server/routes/demo-showcase.js +332 -0
- package/server/routes/demo-store.js +154 -0
- package/server/routes/discovery.js +417 -406
- package/server/routes/gateway.js +173 -0
- package/server/routes/license.js +251 -240
- package/server/routes/mesh.js +469 -469
- package/server/routes/noscript.js +543 -543
- package/server/routes/premium-v2.js +686 -686
- package/server/routes/premium.js +724 -724
- package/server/routes/runtime.js +2148 -2147
- package/server/routes/sovereign.js +465 -385
- package/server/routes/universal.js +200 -177
- package/server/routes/wab-api.js +850 -491
- package/server/runtime/container-worker.js +111 -111
- package/server/runtime/container.js +448 -448
- package/server/runtime/distributed-worker.js +362 -362
- package/server/runtime/event-bus.js +210 -210
- package/server/runtime/index.js +253 -253
- package/server/runtime/queue.js +599 -599
- package/server/runtime/replay.js +666 -666
- package/server/runtime/sandbox.js +266 -266
- package/server/runtime/scheduler.js +534 -534
- package/server/runtime/session-engine.js +293 -293
- package/server/runtime/state-manager.js +188 -188
- package/server/security/cross-site-redactor.js +196 -0
- package/server/security/dry-run.js +180 -0
- package/server/security/human-gate-rate-limit.js +147 -0
- package/server/security/human-gate-transports.js +178 -0
- package/server/security/human-gate.js +281 -0
- package/server/security/index.js +368 -368
- package/server/security/intent-engine.js +245 -0
- package/server/security/reward-guard.js +171 -0
- package/server/security/rollback-store.js +239 -0
- package/server/security/token-scope.js +404 -0
- package/server/security/url-policy.js +139 -0
- package/server/services/agent-chat.js +506 -506
- package/server/services/agent-learning.js +601 -575
- package/server/services/agent-memory.js +625 -625
- package/server/services/agent-mesh.js +555 -539
- package/server/services/agent-symphony.js +717 -717
- package/server/services/agent-tasks.js +1807 -1807
- package/server/services/api-key-engine.js +292 -0
- package/server/services/cluster.js +894 -894
- package/server/services/commander.js +738 -738
- package/server/services/edge-compute.js +440 -440
- package/server/services/email.js +204 -204
- package/server/services/hosted-runtime.js +205 -205
- package/server/services/lfd.js +635 -616
- package/server/services/local-ai.js +389 -389
- package/server/services/marketplace.js +270 -270
- package/server/services/metering.js +182 -182
- 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/negotiation.js +439 -439
- package/server/services/plugins.js +771 -771
- package/server/services/premium.js +1 -1
- package/server/services/price-intelligence.js +566 -565
- package/server/services/price-shield.js +1137 -1137
- package/server/services/reputation.js +465 -465
- package/server/services/search-engine.js +357 -357
- package/server/services/security.js +513 -513
- package/server/services/self-healing.js +843 -843
- package/server/services/sovereign-shield.js +542 -0
- package/server/services/stripe.js +192 -192
- package/server/services/swarm.js +788 -788
- package/server/services/universal-scraper.js +662 -661
- package/server/services/verification.js +481 -481
- package/server/services/vision.js +1163 -1163
- package/server/utils/cache.js +125 -125
- package/server/utils/migrate.js +81 -81
- package/server/utils/safe-fetch.js +228 -0
- package/server/utils/secureFields.js +50 -50
- package/server/ws.js +161 -161
- package/templates/artisan-marketplace.yaml +104 -104
- package/templates/book-price-scout.yaml +98 -98
- package/templates/electronics-price-tracker.yaml +108 -108
- package/templates/flight-deal-hunter.yaml +113 -113
- package/templates/freelancer-direct.yaml +116 -116
- package/templates/grocery-price-compare.yaml +93 -93
- package/templates/hotel-direct-booking.yaml +113 -113
- package/templates/local-services.yaml +98 -98
- package/templates/olive-oil-tunisia.yaml +88 -88
- package/templates/organic-farm-fresh.yaml +101 -101
- package/templates/restaurant-direct.yaml +97 -97
- package/server/services/fairness-engine.js +0 -409
- package/server/services/fairness.js +0 -420
package/server/protocol/index.js
CHANGED
|
@@ -1,243 +1,243 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* WAB Protocol (WABP) - Entry Point
|
|
5
|
-
*
|
|
6
|
-
* The WAB Protocol defines how agents communicate with sites, runtimes,
|
|
7
|
-
* and each other. It is the foundation of the Agent OS.
|
|
8
|
-
*
|
|
9
|
-
* Protocol message envelope:
|
|
10
|
-
* {
|
|
11
|
-
* protocol: 'wabp',
|
|
12
|
-
* version: '1.0.0',
|
|
13
|
-
* id: '<unique-message-id>',
|
|
14
|
-
* type: 'request' | 'response' | 'event' | 'error',
|
|
15
|
-
* command: '<command-name>',
|
|
16
|
-
* agentId: '<agent-id>',
|
|
17
|
-
* traceId: '<trace-id>',
|
|
18
|
-
* spanId: '<span-id>',
|
|
19
|
-
* timestamp: <epoch-ms>,
|
|
20
|
-
* payload: { ... },
|
|
21
|
-
* signature: '<ed25519-signature>' // optional, for signed commands
|
|
22
|
-
* }
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
const crypto = require('crypto');
|
|
26
|
-
const schema = require('./schema');
|
|
27
|
-
const { CapabilityNegotiator } = require('./capabilities');
|
|
28
|
-
|
|
29
|
-
const PROTOCOL_NAME = 'wabp';
|
|
30
|
-
const PROTOCOL_VERSION = schema.PROTOCOL_VERSION;
|
|
31
|
-
|
|
32
|
-
const negotiator = new CapabilityNegotiator();
|
|
33
|
-
|
|
34
|
-
// ─── Message Factory ────────────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
function createMessage(type, command, payload, options = {}) {
|
|
37
|
-
return {
|
|
38
|
-
protocol: PROTOCOL_NAME,
|
|
39
|
-
version: PROTOCOL_VERSION,
|
|
40
|
-
id: `msg_${crypto.randomBytes(16).toString('hex')}`,
|
|
41
|
-
type,
|
|
42
|
-
command,
|
|
43
|
-
agentId: options.agentId || null,
|
|
44
|
-
traceId: options.traceId || `trace_${crypto.randomBytes(16).toString('hex')}`,
|
|
45
|
-
spanId: options.spanId || `span_${crypto.randomBytes(8).toString('hex')}`,
|
|
46
|
-
timestamp: Date.now(),
|
|
47
|
-
payload,
|
|
48
|
-
signature: options.signature || null,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function createRequest(command, payload, options) {
|
|
53
|
-
return createMessage('request', command, payload, options);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function createResponse(requestId, command, payload, options = {}) {
|
|
57
|
-
const msg = createMessage('response', command, payload, options);
|
|
58
|
-
msg.requestId = requestId;
|
|
59
|
-
return msg;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function createError(requestId, command, code, message, options = {}) {
|
|
63
|
-
return createResponse(requestId, command, {
|
|
64
|
-
error: { code, message, timestamp: Date.now() },
|
|
65
|
-
}, options);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function createEvent(eventName, payload, options) {
|
|
69
|
-
return createMessage('event', eventName, payload, options);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ─── Message Validation ─────────────────────────────────────────────────────
|
|
73
|
-
|
|
74
|
-
function validateMessage(msg) {
|
|
75
|
-
const errors = [];
|
|
76
|
-
if (!msg || typeof msg !== 'object') return { valid: false, errors: ['Message must be an object'] };
|
|
77
|
-
if (msg.protocol !== PROTOCOL_NAME) errors.push(`Invalid protocol: expected ${PROTOCOL_NAME}`);
|
|
78
|
-
if (!msg.id) errors.push('Missing message id');
|
|
79
|
-
if (!['request', 'response', 'event', 'error'].includes(msg.type)) errors.push('Invalid message type');
|
|
80
|
-
if (!msg.command) errors.push('Missing command');
|
|
81
|
-
if (!msg.timestamp || typeof msg.timestamp !== 'number') errors.push('Invalid timestamp');
|
|
82
|
-
|
|
83
|
-
// Validate command input for requests
|
|
84
|
-
if (msg.type === 'request' && msg.payload) {
|
|
85
|
-
const cmdValidation = schema.validateInput(msg.command, msg.payload);
|
|
86
|
-
if (!cmdValidation.valid) {
|
|
87
|
-
errors.push(...cmdValidation.errors.map(e => `${e.path}: ${e.message}`));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return { valid: errors.length === 0, errors };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ─── Protocol Handler ───────────────────────────────────────────────────────
|
|
95
|
-
|
|
96
|
-
class ProtocolHandler {
|
|
97
|
-
constructor() {
|
|
98
|
-
this._handlers = new Map(); // command → handler function
|
|
99
|
-
this._middleware = []; // pre-processing middleware
|
|
100
|
-
this._eventListeners = new Map(); // event → listeners
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Register a command handler
|
|
105
|
-
*/
|
|
106
|
-
handle(command, handler) {
|
|
107
|
-
this._handlers.set(command, handler);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Add middleware to the processing pipeline
|
|
112
|
-
*/
|
|
113
|
-
use(middleware) {
|
|
114
|
-
this._middleware.push(middleware);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Subscribe to protocol events
|
|
119
|
-
*/
|
|
120
|
-
on(event, listener) {
|
|
121
|
-
if (!this._eventListeners.has(event)) this._eventListeners.set(event, []);
|
|
122
|
-
this._eventListeners.get(event).push(listener);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Process an incoming protocol message
|
|
127
|
-
*/
|
|
128
|
-
async process(msg) {
|
|
129
|
-
// Validate
|
|
130
|
-
const validation = validateMessage(msg);
|
|
131
|
-
if (!validation.valid) {
|
|
132
|
-
return createError(msg.id, msg.command, 'INVALID_MESSAGE', validation.errors.join('; '));
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Check command exists
|
|
136
|
-
const cmdDef = schema.getCommand(msg.command);
|
|
137
|
-
if (!cmdDef) {
|
|
138
|
-
return createError(msg.id, msg.command, 'UNKNOWN_COMMAND', `Command not found: ${msg.command}`);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Run middleware
|
|
142
|
-
let context = { message: msg, command: cmdDef, metadata: {} };
|
|
143
|
-
for (const mw of this._middleware) {
|
|
144
|
-
try {
|
|
145
|
-
const result = await mw(context);
|
|
146
|
-
if (result === false) {
|
|
147
|
-
return createError(msg.id, msg.command, 'MIDDLEWARE_REJECTED', 'Request rejected by middleware');
|
|
148
|
-
}
|
|
149
|
-
if (result && typeof result === 'object') context = { ...context, ...result };
|
|
150
|
-
} catch (err) {
|
|
151
|
-
return createError(msg.id, msg.command, 'MIDDLEWARE_ERROR', err.message);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Check capability if agent
|
|
156
|
-
if (msg.agentId && cmdDef.capabilities.length > 0) {
|
|
157
|
-
for (const cap of cmdDef.capabilities) {
|
|
158
|
-
if (!negotiator.check(msg.agentId, cap)) {
|
|
159
|
-
return createError(msg.id, msg.command, 'CAPABILITY_DENIED',
|
|
160
|
-
`Agent ${msg.agentId} lacks capability: ${cap}`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Use capabilities
|
|
164
|
-
for (const cap of cmdDef.capabilities) {
|
|
165
|
-
negotiator.use(msg.agentId, cap);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Execute handler
|
|
170
|
-
const handler = this._handlers.get(msg.command);
|
|
171
|
-
if (!handler) {
|
|
172
|
-
return createError(msg.id, msg.command, 'NO_HANDLER', `No handler for: ${msg.command}`);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
try {
|
|
176
|
-
const timeoutMs = msg.payload?.timeout || cmdDef.timeout;
|
|
177
|
-
const result = await _withTimeout(handler(msg.payload, context), timeoutMs);
|
|
178
|
-
const response = createResponse(msg.id, msg.command, result, {
|
|
179
|
-
agentId: msg.agentId,
|
|
180
|
-
traceId: msg.traceId,
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// Emit event
|
|
184
|
-
_emit(this._eventListeners, 'command:complete', {
|
|
185
|
-
command: msg.command,
|
|
186
|
-
agentId: msg.agentId,
|
|
187
|
-
traceId: msg.traceId,
|
|
188
|
-
duration: Date.now() - msg.timestamp,
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
return response;
|
|
192
|
-
} catch (err) {
|
|
193
|
-
_emit(this._eventListeners, 'command:error', {
|
|
194
|
-
command: msg.command,
|
|
195
|
-
agentId: msg.agentId,
|
|
196
|
-
traceId: msg.traceId,
|
|
197
|
-
error: err.message,
|
|
198
|
-
});
|
|
199
|
-
return createError(msg.id, msg.command, 'EXECUTION_ERROR', err.message, {
|
|
200
|
-
traceId: msg.traceId,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
207
|
-
|
|
208
|
-
function _withTimeout(promise, ms) {
|
|
209
|
-
if (!ms || ms <= 0) return promise;
|
|
210
|
-
return new Promise((resolve, reject) => {
|
|
211
|
-
const timer = setTimeout(() => reject(new Error(`Command timed out after ${ms}ms`)), ms);
|
|
212
|
-
promise.then(result => { clearTimeout(timer); resolve(result); })
|
|
213
|
-
.catch(err => { clearTimeout(timer); reject(err); });
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function _emit(listeners, event, data) {
|
|
218
|
-
const fns = listeners.get(event);
|
|
219
|
-
if (!fns) return;
|
|
220
|
-
for (const fn of fns) {
|
|
221
|
-
try { fn(data); } catch (_) { /* ignore listener errors */ }
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// ─── Cleanup interval ──────────────────────────────────────────────────────
|
|
226
|
-
|
|
227
|
-
const _cleanupInterval = setInterval(() => negotiator.cleanup(), 300_000);
|
|
228
|
-
if (_cleanupInterval.unref) _cleanupInterval.unref();
|
|
229
|
-
|
|
230
|
-
module.exports = {
|
|
231
|
-
PROTOCOL_NAME,
|
|
232
|
-
PROTOCOL_VERSION,
|
|
233
|
-
schema,
|
|
234
|
-
negotiator,
|
|
235
|
-
createMessage,
|
|
236
|
-
createRequest,
|
|
237
|
-
createResponse,
|
|
238
|
-
createError,
|
|
239
|
-
createEvent,
|
|
240
|
-
validateMessage,
|
|
241
|
-
ProtocolHandler,
|
|
242
|
-
CapabilityNegotiator: require('./capabilities').CapabilityNegotiator,
|
|
243
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WAB Protocol (WABP) - Entry Point
|
|
5
|
+
*
|
|
6
|
+
* The WAB Protocol defines how agents communicate with sites, runtimes,
|
|
7
|
+
* and each other. It is the foundation of the Agent OS.
|
|
8
|
+
*
|
|
9
|
+
* Protocol message envelope:
|
|
10
|
+
* {
|
|
11
|
+
* protocol: 'wabp',
|
|
12
|
+
* version: '1.0.0',
|
|
13
|
+
* id: '<unique-message-id>',
|
|
14
|
+
* type: 'request' | 'response' | 'event' | 'error',
|
|
15
|
+
* command: '<command-name>',
|
|
16
|
+
* agentId: '<agent-id>',
|
|
17
|
+
* traceId: '<trace-id>',
|
|
18
|
+
* spanId: '<span-id>',
|
|
19
|
+
* timestamp: <epoch-ms>,
|
|
20
|
+
* payload: { ... },
|
|
21
|
+
* signature: '<ed25519-signature>' // optional, for signed commands
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const crypto = require('crypto');
|
|
26
|
+
const schema = require('./schema');
|
|
27
|
+
const { CapabilityNegotiator } = require('./capabilities');
|
|
28
|
+
|
|
29
|
+
const PROTOCOL_NAME = 'wabp';
|
|
30
|
+
const PROTOCOL_VERSION = schema.PROTOCOL_VERSION;
|
|
31
|
+
|
|
32
|
+
const negotiator = new CapabilityNegotiator();
|
|
33
|
+
|
|
34
|
+
// ─── Message Factory ────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
function createMessage(type, command, payload, options = {}) {
|
|
37
|
+
return {
|
|
38
|
+
protocol: PROTOCOL_NAME,
|
|
39
|
+
version: PROTOCOL_VERSION,
|
|
40
|
+
id: `msg_${crypto.randomBytes(16).toString('hex')}`,
|
|
41
|
+
type,
|
|
42
|
+
command,
|
|
43
|
+
agentId: options.agentId || null,
|
|
44
|
+
traceId: options.traceId || `trace_${crypto.randomBytes(16).toString('hex')}`,
|
|
45
|
+
spanId: options.spanId || `span_${crypto.randomBytes(8).toString('hex')}`,
|
|
46
|
+
timestamp: Date.now(),
|
|
47
|
+
payload,
|
|
48
|
+
signature: options.signature || null,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createRequest(command, payload, options) {
|
|
53
|
+
return createMessage('request', command, payload, options);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createResponse(requestId, command, payload, options = {}) {
|
|
57
|
+
const msg = createMessage('response', command, payload, options);
|
|
58
|
+
msg.requestId = requestId;
|
|
59
|
+
return msg;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function createError(requestId, command, code, message, options = {}) {
|
|
63
|
+
return createResponse(requestId, command, {
|
|
64
|
+
error: { code, message, timestamp: Date.now() },
|
|
65
|
+
}, options);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function createEvent(eventName, payload, options) {
|
|
69
|
+
return createMessage('event', eventName, payload, options);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── Message Validation ─────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
function validateMessage(msg) {
|
|
75
|
+
const errors = [];
|
|
76
|
+
if (!msg || typeof msg !== 'object') return { valid: false, errors: ['Message must be an object'] };
|
|
77
|
+
if (msg.protocol !== PROTOCOL_NAME) errors.push(`Invalid protocol: expected ${PROTOCOL_NAME}`);
|
|
78
|
+
if (!msg.id) errors.push('Missing message id');
|
|
79
|
+
if (!['request', 'response', 'event', 'error'].includes(msg.type)) errors.push('Invalid message type');
|
|
80
|
+
if (!msg.command) errors.push('Missing command');
|
|
81
|
+
if (!msg.timestamp || typeof msg.timestamp !== 'number') errors.push('Invalid timestamp');
|
|
82
|
+
|
|
83
|
+
// Validate command input for requests
|
|
84
|
+
if (msg.type === 'request' && msg.payload) {
|
|
85
|
+
const cmdValidation = schema.validateInput(msg.command, msg.payload);
|
|
86
|
+
if (!cmdValidation.valid) {
|
|
87
|
+
errors.push(...cmdValidation.errors.map(e => `${e.path}: ${e.message}`));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { valid: errors.length === 0, errors };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── Protocol Handler ───────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
class ProtocolHandler {
|
|
97
|
+
constructor() {
|
|
98
|
+
this._handlers = new Map(); // command → handler function
|
|
99
|
+
this._middleware = []; // pre-processing middleware
|
|
100
|
+
this._eventListeners = new Map(); // event → listeners
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Register a command handler
|
|
105
|
+
*/
|
|
106
|
+
handle(command, handler) {
|
|
107
|
+
this._handlers.set(command, handler);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Add middleware to the processing pipeline
|
|
112
|
+
*/
|
|
113
|
+
use(middleware) {
|
|
114
|
+
this._middleware.push(middleware);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Subscribe to protocol events
|
|
119
|
+
*/
|
|
120
|
+
on(event, listener) {
|
|
121
|
+
if (!this._eventListeners.has(event)) this._eventListeners.set(event, []);
|
|
122
|
+
this._eventListeners.get(event).push(listener);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Process an incoming protocol message
|
|
127
|
+
*/
|
|
128
|
+
async process(msg) {
|
|
129
|
+
// Validate
|
|
130
|
+
const validation = validateMessage(msg);
|
|
131
|
+
if (!validation.valid) {
|
|
132
|
+
return createError(msg.id, msg.command, 'INVALID_MESSAGE', validation.errors.join('; '));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Check command exists
|
|
136
|
+
const cmdDef = schema.getCommand(msg.command);
|
|
137
|
+
if (!cmdDef) {
|
|
138
|
+
return createError(msg.id, msg.command, 'UNKNOWN_COMMAND', `Command not found: ${msg.command}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Run middleware
|
|
142
|
+
let context = { message: msg, command: cmdDef, metadata: {} };
|
|
143
|
+
for (const mw of this._middleware) {
|
|
144
|
+
try {
|
|
145
|
+
const result = await mw(context);
|
|
146
|
+
if (result === false) {
|
|
147
|
+
return createError(msg.id, msg.command, 'MIDDLEWARE_REJECTED', 'Request rejected by middleware');
|
|
148
|
+
}
|
|
149
|
+
if (result && typeof result === 'object') context = { ...context, ...result };
|
|
150
|
+
} catch (err) {
|
|
151
|
+
return createError(msg.id, msg.command, 'MIDDLEWARE_ERROR', err.message);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Check capability if agent
|
|
156
|
+
if (msg.agentId && cmdDef.capabilities.length > 0) {
|
|
157
|
+
for (const cap of cmdDef.capabilities) {
|
|
158
|
+
if (!negotiator.check(msg.agentId, cap)) {
|
|
159
|
+
return createError(msg.id, msg.command, 'CAPABILITY_DENIED',
|
|
160
|
+
`Agent ${msg.agentId} lacks capability: ${cap}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Use capabilities
|
|
164
|
+
for (const cap of cmdDef.capabilities) {
|
|
165
|
+
negotiator.use(msg.agentId, cap);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Execute handler
|
|
170
|
+
const handler = this._handlers.get(msg.command);
|
|
171
|
+
if (!handler) {
|
|
172
|
+
return createError(msg.id, msg.command, 'NO_HANDLER', `No handler for: ${msg.command}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
const timeoutMs = msg.payload?.timeout || cmdDef.timeout;
|
|
177
|
+
const result = await _withTimeout(handler(msg.payload, context), timeoutMs);
|
|
178
|
+
const response = createResponse(msg.id, msg.command, result, {
|
|
179
|
+
agentId: msg.agentId,
|
|
180
|
+
traceId: msg.traceId,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Emit event
|
|
184
|
+
_emit(this._eventListeners, 'command:complete', {
|
|
185
|
+
command: msg.command,
|
|
186
|
+
agentId: msg.agentId,
|
|
187
|
+
traceId: msg.traceId,
|
|
188
|
+
duration: Date.now() - msg.timestamp,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return response;
|
|
192
|
+
} catch (err) {
|
|
193
|
+
_emit(this._eventListeners, 'command:error', {
|
|
194
|
+
command: msg.command,
|
|
195
|
+
agentId: msg.agentId,
|
|
196
|
+
traceId: msg.traceId,
|
|
197
|
+
error: err.message,
|
|
198
|
+
});
|
|
199
|
+
return createError(msg.id, msg.command, 'EXECUTION_ERROR', err.message, {
|
|
200
|
+
traceId: msg.traceId,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
function _withTimeout(promise, ms) {
|
|
209
|
+
if (!ms || ms <= 0) return promise;
|
|
210
|
+
return new Promise((resolve, reject) => {
|
|
211
|
+
const timer = setTimeout(() => reject(new Error(`Command timed out after ${ms}ms`)), ms);
|
|
212
|
+
promise.then(result => { clearTimeout(timer); resolve(result); })
|
|
213
|
+
.catch(err => { clearTimeout(timer); reject(err); });
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function _emit(listeners, event, data) {
|
|
218
|
+
const fns = listeners.get(event);
|
|
219
|
+
if (!fns) return;
|
|
220
|
+
for (const fn of fns) {
|
|
221
|
+
try { fn(data); } catch (_) { /* ignore listener errors */ }
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ─── Cleanup interval ──────────────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
const _cleanupInterval = setInterval(() => negotiator.cleanup(), 300_000);
|
|
228
|
+
if (_cleanupInterval.unref) _cleanupInterval.unref();
|
|
229
|
+
|
|
230
|
+
module.exports = {
|
|
231
|
+
PROTOCOL_NAME,
|
|
232
|
+
PROTOCOL_VERSION,
|
|
233
|
+
schema,
|
|
234
|
+
negotiator,
|
|
235
|
+
createMessage,
|
|
236
|
+
createRequest,
|
|
237
|
+
createResponse,
|
|
238
|
+
createError,
|
|
239
|
+
createEvent,
|
|
240
|
+
validateMessage,
|
|
241
|
+
ProtocolHandler,
|
|
242
|
+
CapabilityNegotiator: require('./capabilities').CapabilityNegotiator,
|
|
243
|
+
};
|