web-agent-bridge 2.3.1 → 2.5.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 (53) hide show
  1. package/README.ar.md +524 -31
  2. package/README.md +592 -47
  3. package/bin/agent-runner.js +10 -1
  4. package/package.json +1 -1
  5. package/public/agent-workspace.html +347 -0
  6. package/public/browser.html +484 -0
  7. package/public/css/agent-workspace.css +1713 -0
  8. package/public/index.html +94 -0
  9. package/public/js/agent-workspace.js +1740 -0
  10. package/sdk/index.d.ts +253 -0
  11. package/sdk/index.js +360 -1
  12. package/sdk/package.json +1 -1
  13. package/server/config/secrets.js +13 -5
  14. package/server/control-plane/index.js +301 -0
  15. package/server/data-plane/index.js +354 -0
  16. package/server/index.js +185 -4
  17. package/server/llm/index.js +404 -0
  18. package/server/middleware/adminAuth.js +6 -1
  19. package/server/middleware/auth.js +11 -2
  20. package/server/middleware/rateLimits.js +78 -2
  21. package/server/migrations/003_ads_integer_cents.sql +33 -0
  22. package/server/models/db.js +126 -25
  23. package/server/observability/index.js +394 -0
  24. package/server/protocol/capabilities.js +223 -0
  25. package/server/protocol/index.js +243 -0
  26. package/server/protocol/schema.js +584 -0
  27. package/server/registry/index.js +326 -0
  28. package/server/routes/admin.js +16 -2
  29. package/server/routes/ads.js +130 -0
  30. package/server/routes/agent-workspace.js +378 -0
  31. package/server/routes/api.js +21 -2
  32. package/server/routes/auth.js +26 -6
  33. package/server/routes/runtime.js +725 -0
  34. package/server/routes/sovereign.js +78 -0
  35. package/server/routes/universal.js +177 -0
  36. package/server/routes/wab-api.js +20 -5
  37. package/server/runtime/event-bus.js +210 -0
  38. package/server/runtime/index.js +233 -0
  39. package/server/runtime/sandbox.js +266 -0
  40. package/server/runtime/scheduler.js +395 -0
  41. package/server/runtime/state-manager.js +188 -0
  42. package/server/security/index.js +355 -0
  43. package/server/services/agent-chat.js +506 -0
  44. package/server/services/agent-symphony.js +6 -0
  45. package/server/services/agent-tasks.js +1807 -0
  46. package/server/services/fairness-engine.js +409 -0
  47. package/server/services/plugins.js +27 -3
  48. package/server/services/price-intelligence.js +565 -0
  49. package/server/services/price-shield.js +1137 -0
  50. package/server/services/search-engine.js +357 -0
  51. package/server/services/security.js +513 -0
  52. package/server/services/universal-scraper.js +661 -0
  53. package/server/ws.js +61 -1
package/sdk/index.d.ts CHANGED
@@ -209,3 +209,256 @@ export declare class WABMultiAgent {
209
209
  /** Close all browser sessions. */
210
210
  close(): Promise<void>;
211
211
  }
212
+
213
+ // ─── WABUniversalAgent — Works on ANY page, no bridge needed ───────────
214
+
215
+ export interface UniversalAnalysis {
216
+ url: string;
217
+ domain: string;
218
+ products?: Array<{
219
+ name?: string;
220
+ price?: number;
221
+ currency?: string;
222
+ originalPrice?: number;
223
+ rating?: number;
224
+ method?: string;
225
+ }>;
226
+ fairness?: {
227
+ total: number;
228
+ category: string;
229
+ breakdown: Record<string, number>;
230
+ wabBridge?: { installed: boolean; bonus?: number; hasNegotiation?: boolean };
231
+ platform?: { size: string; commission: number };
232
+ };
233
+ darkPatterns?: Array<{ type: string; severity?: string; matches?: string[] }>;
234
+ alerts?: Array<{ title: string; description?: string; severity?: string }>;
235
+ }
236
+
237
+ export interface UniversalDeal {
238
+ name?: string;
239
+ source?: string;
240
+ domain?: string;
241
+ priceUsd?: number;
242
+ rating?: number;
243
+ url?: string;
244
+ compositeScore?: number;
245
+ wabBridge?: boolean;
246
+ canNegotiate?: boolean;
247
+ fairness?: { total: number; category: string };
248
+ }
249
+
250
+ export interface UniversalDealsResult {
251
+ deals: UniversalDeal[];
252
+ insights?: Array<{ icon?: string; text: string }>;
253
+ sourcesChecked?: number;
254
+ }
255
+
256
+ export interface UniversalFairness {
257
+ domain: string;
258
+ total: number;
259
+ category: string;
260
+ breakdown: Record<string, number>;
261
+ wabBridge?: { installed: boolean; bonus?: number };
262
+ platform?: { size: string; commission: number };
263
+ }
264
+
265
+ export declare class WABUniversalAgent {
266
+ constructor(serverUrl?: string);
267
+
268
+ /** Extract products, prices, and metadata from any URL. */
269
+ extract(url: string): Promise<any>;
270
+
271
+ /** Full analysis: extract + fairness + fraud detection + dark patterns. */
272
+ analyze(url: string): Promise<UniversalAnalysis>;
273
+
274
+ /** Compare prices across multiple sources. */
275
+ compare(query: string, category?: string): Promise<any>;
276
+
277
+ /** Find and rank the best deals with fairness scoring. */
278
+ deals(query: string, category?: string, lang?: string): Promise<UniversalDealsResult>;
279
+
280
+ /** Get fairness score for a domain. */
281
+ fairness(domain: string): Promise<UniversalFairness>;
282
+
283
+ /** Detect dark patterns on a URL. */
284
+ darkPatterns(url: string): Promise<any>;
285
+
286
+ /** Get price history for a domain. */
287
+ priceHistory(domain: string): Promise<any>;
288
+
289
+ /** Get top fairness-scored sites. */
290
+ topFair(limit?: number): Promise<any>;
291
+
292
+ /** Get all known competing sources. */
293
+ sources(): Promise<any>;
294
+ }
295
+
296
+ // ─── WABAgentOS — Agent OS Runtime Client ──────────────────────────────
297
+
298
+ export interface WABAgentOSOptions {
299
+ /** WAB server base URL (default http://localhost:3000). */
300
+ serverUrl?: string;
301
+ /** Pre-registered agent ID. */
302
+ agentId?: string;
303
+ /** Pre-registered API key. */
304
+ apiKey?: string;
305
+ /** Active session token. */
306
+ sessionToken?: string;
307
+ }
308
+
309
+ export interface AgentRegistration {
310
+ agentId: string;
311
+ apiKey: string;
312
+ message: string;
313
+ }
314
+
315
+ export interface AgentSession {
316
+ sessionToken?: string;
317
+ agentId: string;
318
+ expiresAt: number;
319
+ }
320
+
321
+ export interface ProtocolInfo {
322
+ protocol: string;
323
+ version: string;
324
+ commands: Array<{
325
+ name: string;
326
+ version: string;
327
+ category: string;
328
+ description: string;
329
+ capabilities: string[];
330
+ }>;
331
+ capabilities: string[];
332
+ permissionLevels: Record<string, number>;
333
+ }
334
+
335
+ export interface TaskSubmitResult {
336
+ taskId: string;
337
+ state: string;
338
+ }
339
+
340
+ export interface TaskInfo {
341
+ taskId: string;
342
+ type: string;
343
+ state: string;
344
+ priority: number;
345
+ result?: any;
346
+ error?: string;
347
+ createdAt: number;
348
+ startedAt?: number;
349
+ completedAt?: number;
350
+ }
351
+
352
+ export interface ExecuteResult {
353
+ success: boolean;
354
+ result?: any;
355
+ error?: string;
356
+ traceId?: string;
357
+ duration?: number;
358
+ }
359
+
360
+ export interface RegistryCommand {
361
+ id: string;
362
+ siteId: string;
363
+ name: string;
364
+ description: string;
365
+ category: string;
366
+ version: string;
367
+ tags: string[];
368
+ usageCount: number;
369
+ }
370
+
371
+ export interface RegistrySite {
372
+ domain: string;
373
+ name: string;
374
+ tier: string;
375
+ capabilities: string[];
376
+ verified: boolean;
377
+ }
378
+
379
+ export interface RegistryTemplate {
380
+ id: string;
381
+ name: string;
382
+ description: string;
383
+ category: string;
384
+ steps: any[];
385
+ downloads: number;
386
+ }
387
+
388
+ export interface HealthInfo {
389
+ runtime: any;
390
+ identity: any;
391
+ registry: any;
392
+ executor: any;
393
+ llm: any;
394
+ }
395
+
396
+ export interface LLMCompletionResult {
397
+ text: string;
398
+ provider: string;
399
+ model: string;
400
+ tokens?: { input: number; output: number; total: number };
401
+ cached?: boolean;
402
+ }
403
+
404
+ export declare class WABAgentOS {
405
+ agentId: string | null;
406
+ apiKey: string | null;
407
+ sessionToken: string | null;
408
+
409
+ constructor(options?: WABAgentOSOptions);
410
+
411
+ // Agent Identity
412
+ register(name: string, type: string, capabilities?: string[]): Promise<AgentRegistration>;
413
+ authenticate(apiKey?: string): Promise<AgentSession>;
414
+ negotiateCapabilities(capabilities: string[], siteId?: string): Promise<any>;
415
+
416
+ // Protocol
417
+ getProtocol(): Promise<ProtocolInfo>;
418
+ sendMessage(command: string, payload?: Record<string, unknown>): Promise<any>;
419
+
420
+ // Tasks
421
+ submitTask(task: Record<string, unknown>): Promise<TaskSubmitResult>;
422
+ getTask(taskId: string): Promise<TaskInfo>;
423
+ listTasks(state?: string, limit?: number): Promise<{ tasks: TaskInfo[]; total: number }>;
424
+ cancelTask(taskId: string): Promise<{ success: boolean }>;
425
+ pauseTask(taskId: string): Promise<{ success: boolean }>;
426
+ resumeTask(taskId: string): Promise<{ success: boolean }>;
427
+
428
+ // Execution
429
+ execute(command: Record<string, unknown>): Promise<ExecuteResult>;
430
+ executeSemantic(domain: string, action: string, params?: Record<string, unknown>): Promise<ExecuteResult>;
431
+ executePipeline(steps: Array<Record<string, unknown>>): Promise<ExecuteResult>;
432
+ resolveAction(domain: string, action: string, siteDomain?: string): Promise<any>;
433
+
434
+ // Registry
435
+ searchCommands(query?: Record<string, string>): Promise<{ commands: RegistryCommand[]; total: number }>;
436
+ registerCommand(siteId: string, command: Record<string, unknown>): Promise<RegistryCommand>;
437
+ searchSites(query?: Record<string, string>): Promise<{ sites: RegistrySite[]; total: number }>;
438
+ registerSite(domain: string, info: Record<string, unknown>): Promise<RegistrySite>;
439
+ searchTemplates(query?: Record<string, string>): Promise<{ templates: RegistryTemplate[]; total: number }>;
440
+ getTemplate(templateId: string): Promise<RegistryTemplate>;
441
+
442
+ // LLM
443
+ complete(prompt: string, options?: Record<string, unknown>): Promise<LLMCompletionResult>;
444
+ embed(text: string, options?: Record<string, unknown>): Promise<any>;
445
+ listModels(): Promise<{ models: any[] }>;
446
+
447
+ // Observability
448
+ getMetrics(): Promise<any>;
449
+ getTraces(query?: Record<string, string>): Promise<any>;
450
+ getTrace(traceId: string): Promise<any>;
451
+ getLogs(query?: Record<string, string>): Promise<any>;
452
+ getHealth(): Promise<HealthInfo>;
453
+
454
+ // Events (SSE)
455
+ subscribe(filter: string | null, onEvent: (data: any) => void): EventSource;
456
+
457
+ // Policies
458
+ createPolicy(policy: Record<string, unknown>): Promise<any>;
459
+ evaluatePolicy(entityId: string, action: string, context?: Record<string, unknown>): Promise<any>;
460
+
461
+ // Deploy
462
+ deploy(config?: Record<string, unknown>): Promise<any>;
463
+ listDeployments(query?: Record<string, string>): Promise<any>;
464
+ }
package/sdk/index.js CHANGED
@@ -254,7 +254,366 @@ class WABAgent {
254
254
  }
255
255
  }
256
256
 
257
+ /**
258
+ * WABUniversalAgent — Works on ANY page, no bridge script needed.
259
+ * Uses server-side extraction, analysis, and comparison APIs.
260
+ */
261
+ class WABUniversalAgent {
262
+ /**
263
+ * @param {string} [serverUrl='http://localhost:3000'] — WAB server URL
264
+ */
265
+ constructor(serverUrl = 'http://localhost:3000') {
266
+ this.serverUrl = serverUrl.replace(/\/$/, '');
267
+ }
268
+
269
+ /** @private */
270
+ async _post(path, body) {
271
+ const res = await fetch(`${this.serverUrl}${path}`, {
272
+ method: 'POST',
273
+ headers: { 'Content-Type': 'application/json' },
274
+ body: JSON.stringify(body),
275
+ });
276
+ if (!res.ok) throw new Error(`WAB API error ${res.status}: ${await res.text()}`);
277
+ return res.json();
278
+ }
279
+
280
+ /** @private */
281
+ async _get(path) {
282
+ const res = await fetch(`${this.serverUrl}${path}`);
283
+ if (!res.ok) throw new Error(`WAB API error ${res.status}: ${await res.text()}`);
284
+ return res.json();
285
+ }
286
+
287
+ /**
288
+ * Extract products, prices, and metadata from any URL.
289
+ * @param {string} url
290
+ * @returns {Promise<object>}
291
+ */
292
+ async extract(url) {
293
+ return this._post('/api/universal/extract', { url });
294
+ }
295
+
296
+ /**
297
+ * Full analysis: extract + fairness + fraud detection + dark patterns.
298
+ * @param {string} url
299
+ * @returns {Promise<object>}
300
+ */
301
+ async analyze(url) {
302
+ return this._post('/api/universal/analyze', { url });
303
+ }
304
+
305
+ /**
306
+ * Compare prices across multiple sources.
307
+ * @param {string} query — Product or service to search for
308
+ * @param {string} [category='product'] — 'product', 'hotel', 'flight'
309
+ * @returns {Promise<object>}
310
+ */
311
+ async compare(query, category = 'product') {
312
+ return this._post('/api/universal/compare', { query, category });
313
+ }
314
+
315
+ /**
316
+ * Find and rank the best deals with fairness scoring.
317
+ * @param {string} query
318
+ * @param {string} [category='product']
319
+ * @param {string} [lang='en']
320
+ * @returns {Promise<object>}
321
+ */
322
+ async deals(query, category = 'product', lang = 'en') {
323
+ return this._post('/api/universal/deals', { query, category, lang });
324
+ }
325
+
326
+ /**
327
+ * Get fairness score for a domain.
328
+ * @param {string} domain
329
+ * @returns {Promise<object>}
330
+ */
331
+ async fairness(domain) {
332
+ return this._post('/api/universal/fairness', { domain });
333
+ }
334
+
335
+ /**
336
+ * Detect dark patterns on a URL.
337
+ * @param {string} url
338
+ * @returns {Promise<object>}
339
+ */
340
+ async darkPatterns(url) {
341
+ return this._post('/api/universal/dark-patterns', { url });
342
+ }
343
+
344
+ /**
345
+ * Get price history for a domain.
346
+ * @param {string} domain
347
+ * @returns {Promise<object>}
348
+ */
349
+ async priceHistory(domain) {
350
+ return this._get(`/api/universal/history?domain=${encodeURIComponent(domain)}`);
351
+ }
352
+
353
+ /**
354
+ * Get top fairness-scored sites.
355
+ * @param {number} [limit=20]
356
+ * @returns {Promise<object>}
357
+ */
358
+ async topFair(limit = 20) {
359
+ return this._get(`/api/universal/top-fair?limit=${limit}`);
360
+ }
361
+
362
+ /**
363
+ * Get all known competing sources.
364
+ * @returns {Promise<object>}
365
+ */
366
+ async sources() {
367
+ return this._get('/api/universal/sources');
368
+ }
369
+ }
370
+
257
371
  const { WABMultiAgent } = require('./multi-agent');
258
372
  const { WABAgentMesh } = require('./agent-mesh');
259
373
 
260
- module.exports = { WABAgent, WABMultiAgent, WABAgentMesh };
374
+ // ─── WAB Agent OS Client ────────────────────────────────────────────────────
375
+
376
+ /**
377
+ * WABAgentOS — Client for the Agent OS runtime.
378
+ * Provides access to protocol, tasks, execution, registry, observability, and LLM.
379
+ */
380
+ class WABAgentOS {
381
+ /**
382
+ * @param {object} options
383
+ * @param {string} options.serverUrl — WAB server base URL
384
+ * @param {string} [options.agentId] — Pre-registered agent ID
385
+ * @param {string} [options.apiKey] — Pre-registered API key
386
+ * @param {string} [options.sessionToken] — Active session token
387
+ */
388
+ constructor(options = {}) {
389
+ this.serverUrl = (options.serverUrl || 'http://localhost:3000').replace(/\/$/, '');
390
+ this.agentId = options.agentId || null;
391
+ this.apiKey = options.apiKey || null;
392
+ this.sessionToken = options.sessionToken || null;
393
+ this._base = `${this.serverUrl}/api/os`;
394
+ }
395
+
396
+ // ─── Agent Identity ───────────────────────────────────────────────────
397
+
398
+ async register(name, type, capabilities = []) {
399
+ const res = await this._post('/agents/register', { name, type, capabilities });
400
+ this.agentId = res.agentId;
401
+ this.apiKey = res.apiKey;
402
+ return res;
403
+ }
404
+
405
+ async authenticate(apiKey) {
406
+ const res = await this._post('/agents/authenticate', { apiKey: apiKey || this.apiKey });
407
+ if (res.sessionToken) this.sessionToken = res.sessionToken;
408
+ return res;
409
+ }
410
+
411
+ async negotiateCapabilities(capabilities, siteId) {
412
+ return this._post(`/agents/${this.agentId}/capabilities`, { capabilities, siteId });
413
+ }
414
+
415
+ // ─── Protocol ─────────────────────────────────────────────────────────
416
+
417
+ async getProtocol() {
418
+ return this._get('/protocol');
419
+ }
420
+
421
+ async sendMessage(command, payload = {}) {
422
+ return this._post('/protocol/message', { command, payload, agentId: this.agentId });
423
+ }
424
+
425
+ // ─── Tasks ────────────────────────────────────────────────────────────
426
+
427
+ async submitTask(task) {
428
+ return this._post('/tasks', task);
429
+ }
430
+
431
+ async getTask(taskId) {
432
+ return this._get(`/tasks/${taskId}`);
433
+ }
434
+
435
+ async listTasks(state, limit) {
436
+ const params = [];
437
+ if (state) params.push(`state=${state}`);
438
+ if (limit) params.push(`limit=${limit}`);
439
+ return this._get(`/tasks${params.length ? '?' + params.join('&') : ''}`);
440
+ }
441
+
442
+ async cancelTask(taskId) {
443
+ return this._delete(`/tasks/${taskId}`);
444
+ }
445
+
446
+ async pauseTask(taskId) {
447
+ return this._post(`/tasks/${taskId}/pause`);
448
+ }
449
+
450
+ async resumeTask(taskId) {
451
+ return this._post(`/tasks/${taskId}/resume`);
452
+ }
453
+
454
+ // ─── Execution ────────────────────────────────────────────────────────
455
+
456
+ async execute(command) {
457
+ return this._post('/execute', command);
458
+ }
459
+
460
+ async executeSemantic(domain, action, params = {}) {
461
+ return this._post('/execute/semantic', { domain, action, params, agentId: this.agentId });
462
+ }
463
+
464
+ async executePipeline(steps) {
465
+ return this._post('/execute/pipeline', { steps });
466
+ }
467
+
468
+ async resolveAction(domain, action, siteDomain) {
469
+ const params = `domain=${domain}&action=${action}${siteDomain ? `&siteDomain=${siteDomain}` : ''}`;
470
+ return this._get(`/execute/resolve?${params}`);
471
+ }
472
+
473
+ // ─── Registry ─────────────────────────────────────────────────────────
474
+
475
+ async searchCommands(query = {}) {
476
+ const params = Object.entries(query).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&');
477
+ return this._get(`/registry/commands${params ? '?' + params : ''}`);
478
+ }
479
+
480
+ async registerCommand(siteId, command) {
481
+ return this._post('/registry/commands', { siteId, ...command });
482
+ }
483
+
484
+ async searchSites(query = {}) {
485
+ const params = Object.entries(query).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&');
486
+ return this._get(`/registry/sites${params ? '?' + params : ''}`);
487
+ }
488
+
489
+ async registerSite(domain, info) {
490
+ return this._post('/registry/sites', { domain, ...info });
491
+ }
492
+
493
+ async searchTemplates(query = {}) {
494
+ const params = Object.entries(query).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&');
495
+ return this._get(`/registry/templates${params ? '?' + params : ''}`);
496
+ }
497
+
498
+ async getTemplate(templateId) {
499
+ return this._get(`/registry/templates/${templateId}`);
500
+ }
501
+
502
+ // ─── LLM ──────────────────────────────────────────────────────────────
503
+
504
+ async complete(prompt, options = {}) {
505
+ return this._post('/llm/complete', { prompt, options });
506
+ }
507
+
508
+ async embed(text, options = {}) {
509
+ return this._post('/llm/embed', { text, options });
510
+ }
511
+
512
+ async listModels() {
513
+ return this._get('/llm/models');
514
+ }
515
+
516
+ // ─── Observability ────────────────────────────────────────────────────
517
+
518
+ async getMetrics() {
519
+ return this._get('/observability/metrics');
520
+ }
521
+
522
+ async getTraces(query = {}) {
523
+ const params = Object.entries(query).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&');
524
+ return this._get(`/observability/traces${params ? '?' + params : ''}`);
525
+ }
526
+
527
+ async getTrace(traceId) {
528
+ return this._get(`/observability/traces/${traceId}`);
529
+ }
530
+
531
+ async getLogs(query = {}) {
532
+ const params = Object.entries(query).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&');
533
+ return this._get(`/observability/logs${params ? '?' + params : ''}`);
534
+ }
535
+
536
+ async getHealth() {
537
+ return this._get('/observability/health');
538
+ }
539
+
540
+ // ─── Events (SSE) ────────────────────────────────────────────────────
541
+
542
+ subscribe(filter, onEvent) {
543
+ if (typeof EventSource === 'undefined') {
544
+ throw new Error('EventSource not available. Use a polyfill for Node.js.');
545
+ }
546
+ const url = `${this._base}/events${filter ? '?filter=' + encodeURIComponent(filter) : ''}`;
547
+ const es = new EventSource(url);
548
+ es.onmessage = (e) => {
549
+ try { onEvent(JSON.parse(e.data)); } catch { onEvent(e.data); }
550
+ };
551
+ return es; // Call es.close() to unsubscribe
552
+ }
553
+
554
+ // ─── Policies ─────────────────────────────────────────────────────────
555
+
556
+ async createPolicy(policy) {
557
+ return this._post('/policies', policy);
558
+ }
559
+
560
+ async evaluatePolicy(entityId, action, context = {}) {
561
+ return this._post('/policies/evaluate', { entityId, action, context });
562
+ }
563
+
564
+ // ─── Deploy ───────────────────────────────────────────────────────────
565
+
566
+ async deploy(config = {}) {
567
+ return this._post('/deployments', { agentId: this.agentId, config });
568
+ }
569
+
570
+ async listDeployments(query = {}) {
571
+ const params = Object.entries(query).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&');
572
+ return this._get(`/deployments${params ? '?' + params : ''}`);
573
+ }
574
+
575
+ // ─── HTTP Helpers ─────────────────────────────────────────────────────
576
+
577
+ async _get(path) {
578
+ const url = `${this._base}${path}`;
579
+ const headers = this._headers();
580
+ const res = await fetch(url, { headers });
581
+ if (!res.ok) {
582
+ const body = await res.json().catch(() => ({ error: res.statusText }));
583
+ throw new Error(body.error || `HTTP ${res.status}`);
584
+ }
585
+ return res.json();
586
+ }
587
+
588
+ async _post(path, body) {
589
+ const url = `${this._base}${path}`;
590
+ const headers = { ...this._headers(), 'Content-Type': 'application/json' };
591
+ const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(body) });
592
+ if (!res.ok) {
593
+ const data = await res.json().catch(() => ({ error: res.statusText }));
594
+ throw new Error(data.error || `HTTP ${res.status}`);
595
+ }
596
+ return res.json();
597
+ }
598
+
599
+ async _delete(path) {
600
+ const url = `${this._base}${path}`;
601
+ const headers = this._headers();
602
+ const res = await fetch(url, { method: 'DELETE', headers });
603
+ if (!res.ok) {
604
+ const data = await res.json().catch(() => ({ error: res.statusText }));
605
+ throw new Error(data.error || `HTTP ${res.status}`);
606
+ }
607
+ return res.json();
608
+ }
609
+
610
+ _headers() {
611
+ const h = {};
612
+ if (this.sessionToken) h['Authorization'] = `Bearer ${this.sessionToken}`;
613
+ else if (this.apiKey) h['X-WAB-Key'] = this.apiKey;
614
+ if (this.agentId) h['X-WAB-Agent'] = this.agentId;
615
+ return h;
616
+ }
617
+ }
618
+
619
+ module.exports = { WABAgent, WABUniversalAgent, WABMultiAgent, WABAgentMesh, WABAgentOS };
package/sdk/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-agent-bridge-sdk",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "SDK for building AI agents that interact with Web Agent Bridge (WAB)",
5
5
  "main": "index.js",
6
6
  "license": "MIT",
@@ -33,10 +33,12 @@ function isProd() {
33
33
  function assertSecretsAtStartup() {
34
34
  if (isTest()) return;
35
35
  if (isProd() && !process.env.JWT_SECRET) {
36
- _autoUserSecret = generateAutoSecret('JWT_SECRET');
36
+ console.error('[WAB] FATAL: JWT_SECRET is not set in production. Refusing to start with insecure defaults.');
37
+ process.exit(1);
37
38
  }
38
39
  if (isProd() && !process.env.JWT_SECRET_ADMIN) {
39
- _autoAdminSecret = generateAutoSecret('JWT_SECRET_ADMIN');
40
+ console.error('[WAB] FATAL: JWT_SECRET_ADMIN is not set in production. Refusing to start with insecure defaults.');
41
+ process.exit(1);
40
42
  }
41
43
  }
42
44
 
@@ -44,14 +46,20 @@ function getJwtUserSecret() {
44
46
  if (isTest()) {
45
47
  return process.env.JWT_SECRET || 'test-secret-key-for-testing';
46
48
  }
47
- return process.env.JWT_SECRET || _autoUserSecret || 'dev-user-secret-change-in-development';
49
+ if (process.env.JWT_SECRET) return process.env.JWT_SECRET;
50
+ // Dev mode: generate ephemeral secret per process (not hardcoded)
51
+ if (!_autoUserSecret) _autoUserSecret = generateAutoSecret('JWT_SECRET');
52
+ return _autoUserSecret;
48
53
  }
49
54
 
50
55
  function getJwtAdminSecret() {
51
56
  if (isTest()) {
52
- return process.env.JWT_SECRET_ADMIN || process.env.JWT_SECRET || 'test-secret-key-for-testing-admin';
57
+ return process.env.JWT_SECRET_ADMIN || 'test-secret-key-for-testing-admin';
53
58
  }
54
- return process.env.JWT_SECRET_ADMIN || process.env.JWT_SECRET || _autoAdminSecret || _autoUserSecret || 'dev-admin-secret-change-in-development';
59
+ if (process.env.JWT_SECRET_ADMIN) return process.env.JWT_SECRET_ADMIN;
60
+ // Dev mode: generate separate ephemeral secret (never share with user secret)
61
+ if (!_autoAdminSecret) _autoAdminSecret = generateAutoSecret('JWT_SECRET_ADMIN');
62
+ return _autoAdminSecret;
55
63
  }
56
64
 
57
65
  function signUserToken(payload, options = {}) {