web-agent-bridge 1.2.0 → 2.1.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 +572 -446
- package/README.md +968 -933
- package/bin/agent-runner.js +465 -0
- package/bin/cli.js +138 -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 +71 -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 +581 -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 +405 -0
- package/public/sitemap.xml +93 -0
- package/public/sovereign.html +660 -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 +2 -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/sovereign.js +307 -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/negotiation.js +439 -0
- package/server/services/plugins.js +747 -0
- package/server/services/premium.js +1883 -0
- package/server/services/reputation.js +465 -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/verification.js +481 -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/templates/artisan-marketplace.yaml +104 -0
- package/templates/book-price-scout.yaml +98 -0
- package/templates/electronics-price-tracker.yaml +108 -0
- package/templates/flight-deal-hunter.yaml +113 -0
- package/templates/freelancer-direct.yaml +116 -0
- package/templates/grocery-price-compare.yaml +93 -0
- package/templates/hotel-direct-booking.yaml +113 -0
- package/templates/local-services.yaml +98 -0
- package/templates/olive-oil-tunisia.yaml +88 -0
- package/templates/organic-farm-fresh.yaml +101 -0
- package/templates/restaurant-direct.yaml +97 -0
- 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 };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# WAB Agent Template — Artisan Marketplace Scout
|
|
2
|
+
# Discovers handmade products from small artisans worldwide
|
|
3
|
+
# Run: npx wab-agent run artisan-marketplace.yaml
|
|
4
|
+
|
|
5
|
+
name: artisan-marketplace
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
description: Discover and buy handmade products directly from artisans, not intermediaries
|
|
8
|
+
author: WAB Community
|
|
9
|
+
tags: [artisan, handmade, fair-trade, crafts, direct-buy]
|
|
10
|
+
|
|
11
|
+
goal: >
|
|
12
|
+
Find WAB-enabled artisan shops, evaluate product authenticity and craftsmanship,
|
|
13
|
+
compare prices against mass-produced alternatives, negotiate fair prices that
|
|
14
|
+
support artisans, and verify product descriptions are accurate.
|
|
15
|
+
|
|
16
|
+
target_sites:
|
|
17
|
+
discovery_method: wab-registry
|
|
18
|
+
category: artisan-crafts
|
|
19
|
+
region: global
|
|
20
|
+
fallback_urls: []
|
|
21
|
+
|
|
22
|
+
parameters:
|
|
23
|
+
- name: product_type
|
|
24
|
+
type: string
|
|
25
|
+
required: true
|
|
26
|
+
description: "Type of handmade product (e.g. pottery, textiles, jewelry, leather)"
|
|
27
|
+
- name: origin_country
|
|
28
|
+
type: string
|
|
29
|
+
description: Preferred country of origin
|
|
30
|
+
- name: max_price
|
|
31
|
+
type: number
|
|
32
|
+
default: 500
|
|
33
|
+
|
|
34
|
+
actions:
|
|
35
|
+
- name: discover
|
|
36
|
+
description: Find artisan shops selling the target product type
|
|
37
|
+
wab_action: discover
|
|
38
|
+
|
|
39
|
+
- name: browse_products
|
|
40
|
+
description: Browse available handmade products
|
|
41
|
+
wab_action: getProducts
|
|
42
|
+
params:
|
|
43
|
+
category: "{{product_type}}"
|
|
44
|
+
handmade: true
|
|
45
|
+
|
|
46
|
+
- name: verify_authenticity
|
|
47
|
+
description: Check product descriptions against photos
|
|
48
|
+
wab_action: verifyText
|
|
49
|
+
params:
|
|
50
|
+
fields: [material, origin, technique, dimensions]
|
|
51
|
+
|
|
52
|
+
- name: check_artisan_profile
|
|
53
|
+
description: Verify the artisan is a real person/workshop
|
|
54
|
+
wab_action: getArtisanProfile
|
|
55
|
+
|
|
56
|
+
- name: compare_prices
|
|
57
|
+
description: Compare with mass-produced alternatives for fair pricing
|
|
58
|
+
wab_action: getPrice
|
|
59
|
+
collect: true
|
|
60
|
+
|
|
61
|
+
- name: negotiate
|
|
62
|
+
description: Negotiate a fair price supporting the artisan
|
|
63
|
+
wab_action: negotiate
|
|
64
|
+
strategy: first_time
|
|
65
|
+
conditions:
|
|
66
|
+
proposed_discount: 5
|
|
67
|
+
fallback_strategy: instant_payment
|
|
68
|
+
|
|
69
|
+
- name: purchase
|
|
70
|
+
description: Purchase at agreed price
|
|
71
|
+
wab_action: buy
|
|
72
|
+
requires: [verify_authenticity]
|
|
73
|
+
|
|
74
|
+
fairness_rules:
|
|
75
|
+
prefer_local: false
|
|
76
|
+
prefer_small_business: true
|
|
77
|
+
max_price: "{{max_price}}"
|
|
78
|
+
currency: USD
|
|
79
|
+
min_reputation_score: 30
|
|
80
|
+
avoid_monopolies: true
|
|
81
|
+
support_artisans: true
|
|
82
|
+
|
|
83
|
+
negotiation:
|
|
84
|
+
enabled: true
|
|
85
|
+
max_rounds: 2
|
|
86
|
+
strategies:
|
|
87
|
+
- first_time
|
|
88
|
+
- instant_payment
|
|
89
|
+
- bulk_order
|
|
90
|
+
walk_away_threshold: 3
|
|
91
|
+
respect_artisan_floor: true
|
|
92
|
+
|
|
93
|
+
verification:
|
|
94
|
+
anti_hallucination: true
|
|
95
|
+
cross_check_prices: true
|
|
96
|
+
verify_descriptions: true
|
|
97
|
+
require_vision_match: true
|
|
98
|
+
max_price_variance: 0.15
|
|
99
|
+
|
|
100
|
+
output:
|
|
101
|
+
format: json
|
|
102
|
+
include: [artisan_name, product, material, origin, price, negotiated_price, authenticity_score, reputation]
|
|
103
|
+
sort_by: authenticity_score
|
|
104
|
+
limit: 15
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# WAB Agent Template — Book Price Scout
|
|
2
|
+
# Finds the cheapest books directly from independent bookstores
|
|
3
|
+
# Run: npx wab-agent run book-price-scout.yaml --title "Clean Code"
|
|
4
|
+
|
|
5
|
+
name: book-price-scout
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
description: Find books at the best price from independent bookstores, not just Amazon
|
|
8
|
+
author: WAB Community
|
|
9
|
+
tags: [books, price-comparison, indie-bookstore, shopping]
|
|
10
|
+
|
|
11
|
+
goal: >
|
|
12
|
+
Search WAB-enabled independent bookstores for a specific book,
|
|
13
|
+
compare prices with Amazon, negotiate direct purchase discounts,
|
|
14
|
+
and verify book details are accurate.
|
|
15
|
+
|
|
16
|
+
target_sites:
|
|
17
|
+
discovery_method: wab-registry
|
|
18
|
+
category: books
|
|
19
|
+
region: global
|
|
20
|
+
fallback_urls: []
|
|
21
|
+
|
|
22
|
+
parameters:
|
|
23
|
+
- name: title
|
|
24
|
+
type: string
|
|
25
|
+
required: true
|
|
26
|
+
- name: author
|
|
27
|
+
type: string
|
|
28
|
+
- name: isbn
|
|
29
|
+
type: string
|
|
30
|
+
- name: format
|
|
31
|
+
type: string
|
|
32
|
+
default: paperback
|
|
33
|
+
enum: [paperback, hardcover, ebook]
|
|
34
|
+
|
|
35
|
+
actions:
|
|
36
|
+
- name: discover
|
|
37
|
+
description: Find independent bookstores
|
|
38
|
+
wab_action: discover
|
|
39
|
+
|
|
40
|
+
- name: search_book
|
|
41
|
+
description: Search for the book
|
|
42
|
+
wab_action: searchProducts
|
|
43
|
+
params:
|
|
44
|
+
title: "{{title}}"
|
|
45
|
+
author: "{{author}}"
|
|
46
|
+
isbn: "{{isbn}}"
|
|
47
|
+
collect: true
|
|
48
|
+
|
|
49
|
+
- name: verify_details
|
|
50
|
+
description: Verify book title, author, edition match
|
|
51
|
+
wab_action: verifyText
|
|
52
|
+
require_pass: true
|
|
53
|
+
|
|
54
|
+
- name: verify_prices
|
|
55
|
+
description: Cross-check displayed prices
|
|
56
|
+
wab_action: verifyPrice
|
|
57
|
+
require_pass: true
|
|
58
|
+
|
|
59
|
+
- name: negotiate
|
|
60
|
+
description: Negotiate direct purchase discount
|
|
61
|
+
wab_action: negotiate
|
|
62
|
+
strategy: instant_payment
|
|
63
|
+
conditions:
|
|
64
|
+
proposed_discount: 8
|
|
65
|
+
fallback_strategy: first_time
|
|
66
|
+
|
|
67
|
+
- name: purchase
|
|
68
|
+
description: Buy the book
|
|
69
|
+
wab_action: buy
|
|
70
|
+
requires: [verify_details, verify_prices]
|
|
71
|
+
|
|
72
|
+
fairness_rules:
|
|
73
|
+
prefer_local: true
|
|
74
|
+
prefer_small_business: true
|
|
75
|
+
currency: USD
|
|
76
|
+
min_reputation_score: 25
|
|
77
|
+
avoid_monopolies: true
|
|
78
|
+
|
|
79
|
+
negotiation:
|
|
80
|
+
enabled: true
|
|
81
|
+
max_rounds: 2
|
|
82
|
+
strategies:
|
|
83
|
+
- instant_payment
|
|
84
|
+
- first_time
|
|
85
|
+
- bulk_order
|
|
86
|
+
walk_away_threshold: 3
|
|
87
|
+
|
|
88
|
+
verification:
|
|
89
|
+
anti_hallucination: true
|
|
90
|
+
cross_check_prices: true
|
|
91
|
+
verify_descriptions: true
|
|
92
|
+
max_price_variance: 0.05
|
|
93
|
+
|
|
94
|
+
output:
|
|
95
|
+
format: table
|
|
96
|
+
include: [bookstore, title, author, format, price, negotiated_price, shipping, total, reputation]
|
|
97
|
+
sort_by: total
|
|
98
|
+
limit: 10
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# WAB Agent Template — Electronics Price Tracker
|
|
2
|
+
# Monitors electronics prices and finds the best deal
|
|
3
|
+
# Run: npx wab-agent run electronics-price-tracker.yaml --product "laptop" --max-price 1000
|
|
4
|
+
|
|
5
|
+
name: electronics-price-tracker
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
description: Track electronics prices across independent stores and find the best verified deal
|
|
8
|
+
author: WAB Community
|
|
9
|
+
tags: [electronics, price-tracking, shopping, deals, comparison]
|
|
10
|
+
|
|
11
|
+
goal: >
|
|
12
|
+
Search WAB-enabled electronics retailers, track price history,
|
|
13
|
+
verify prices against screenshots, find the lowest verified price,
|
|
14
|
+
and negotiate bundle or loyalty discounts.
|
|
15
|
+
|
|
16
|
+
target_sites:
|
|
17
|
+
discovery_method: wab-registry
|
|
18
|
+
category: electronics
|
|
19
|
+
region: auto
|
|
20
|
+
fallback_urls: []
|
|
21
|
+
|
|
22
|
+
parameters:
|
|
23
|
+
- name: product
|
|
24
|
+
type: string
|
|
25
|
+
required: true
|
|
26
|
+
description: Product to search for
|
|
27
|
+
- name: max_price
|
|
28
|
+
type: number
|
|
29
|
+
required: true
|
|
30
|
+
- name: brand
|
|
31
|
+
type: string
|
|
32
|
+
- name: condition
|
|
33
|
+
type: string
|
|
34
|
+
default: new
|
|
35
|
+
enum: [new, refurbished, used]
|
|
36
|
+
|
|
37
|
+
actions:
|
|
38
|
+
- name: discover
|
|
39
|
+
description: Find electronics stores
|
|
40
|
+
wab_action: discover
|
|
41
|
+
|
|
42
|
+
- name: search
|
|
43
|
+
description: Search for the product
|
|
44
|
+
wab_action: searchProducts
|
|
45
|
+
params:
|
|
46
|
+
query: "{{product}}"
|
|
47
|
+
brand: "{{brand}}"
|
|
48
|
+
condition: "{{condition}}"
|
|
49
|
+
collect: true
|
|
50
|
+
|
|
51
|
+
- name: verify_prices
|
|
52
|
+
description: Cross-verify all prices
|
|
53
|
+
wab_action: verifyPrice
|
|
54
|
+
require_pass: true
|
|
55
|
+
|
|
56
|
+
- name: check_specs
|
|
57
|
+
description: Verify product specifications match listing
|
|
58
|
+
wab_action: verifyText
|
|
59
|
+
params:
|
|
60
|
+
fields: [model, specs, warranty, condition]
|
|
61
|
+
|
|
62
|
+
- name: check_price_history
|
|
63
|
+
description: Check if current price is a real deal or inflated
|
|
64
|
+
wab_action: getPriceHistory
|
|
65
|
+
collect: true
|
|
66
|
+
|
|
67
|
+
- name: negotiate
|
|
68
|
+
description: Negotiate a discount
|
|
69
|
+
wab_action: negotiate
|
|
70
|
+
strategy: instant_payment
|
|
71
|
+
conditions:
|
|
72
|
+
proposed_discount: 7
|
|
73
|
+
fallback_strategy: first_time
|
|
74
|
+
|
|
75
|
+
- name: purchase
|
|
76
|
+
description: Buy at best verified price
|
|
77
|
+
wab_action: buy
|
|
78
|
+
requires: [verify_prices, check_specs]
|
|
79
|
+
|
|
80
|
+
fairness_rules:
|
|
81
|
+
prefer_local: true
|
|
82
|
+
prefer_small_business: true
|
|
83
|
+
max_price: "{{max_price}}"
|
|
84
|
+
currency: USD
|
|
85
|
+
min_reputation_score: 45
|
|
86
|
+
avoid_monopolies: true
|
|
87
|
+
|
|
88
|
+
negotiation:
|
|
89
|
+
enabled: true
|
|
90
|
+
max_rounds: 3
|
|
91
|
+
strategies:
|
|
92
|
+
- instant_payment
|
|
93
|
+
- first_time
|
|
94
|
+
- bulk_order
|
|
95
|
+
walk_away_threshold: 3
|
|
96
|
+
|
|
97
|
+
verification:
|
|
98
|
+
anti_hallucination: true
|
|
99
|
+
cross_check_prices: true
|
|
100
|
+
require_vision_match: true
|
|
101
|
+
max_price_variance: 0.05
|
|
102
|
+
track_price_history: true
|
|
103
|
+
|
|
104
|
+
output:
|
|
105
|
+
format: table
|
|
106
|
+
include: [store, product, brand, price, price_history_trend, negotiated_price, warranty, reputation, verified]
|
|
107
|
+
sort_by: price
|
|
108
|
+
limit: 15
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# WAB Agent Template — Flight Deal Hunter
|
|
2
|
+
# Finds flights directly from airline websites
|
|
3
|
+
# Run: npx wab-agent run flight-deal-hunter.yaml --from "TUN" --to "CDG" --date "2026-06-15"
|
|
4
|
+
|
|
5
|
+
name: flight-deal-hunter
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
description: Find flight deals directly from airline websites, bypassing aggregator markups
|
|
8
|
+
author: WAB Community
|
|
9
|
+
tags: [travel, flights, airline, direct-booking, savings]
|
|
10
|
+
|
|
11
|
+
goal: >
|
|
12
|
+
Search WAB-enabled airline websites for direct flight bookings,
|
|
13
|
+
compare with aggregator prices, verify fares include all taxes,
|
|
14
|
+
and book at the best direct price.
|
|
15
|
+
|
|
16
|
+
target_sites:
|
|
17
|
+
discovery_method: wab-registry
|
|
18
|
+
category: airlines
|
|
19
|
+
region: global
|
|
20
|
+
fallback_urls: []
|
|
21
|
+
|
|
22
|
+
parameters:
|
|
23
|
+
- name: from
|
|
24
|
+
type: string
|
|
25
|
+
required: true
|
|
26
|
+
description: Departure airport code (IATA)
|
|
27
|
+
- name: to
|
|
28
|
+
type: string
|
|
29
|
+
required: true
|
|
30
|
+
description: Arrival airport code (IATA)
|
|
31
|
+
- name: date
|
|
32
|
+
type: date
|
|
33
|
+
required: true
|
|
34
|
+
- name: return_date
|
|
35
|
+
type: date
|
|
36
|
+
- name: passengers
|
|
37
|
+
type: number
|
|
38
|
+
default: 1
|
|
39
|
+
- name: class
|
|
40
|
+
type: string
|
|
41
|
+
default: economy
|
|
42
|
+
enum: [economy, business, first]
|
|
43
|
+
|
|
44
|
+
actions:
|
|
45
|
+
- name: discover
|
|
46
|
+
description: Find airlines serving this route
|
|
47
|
+
wab_action: discover
|
|
48
|
+
|
|
49
|
+
- name: search_flights
|
|
50
|
+
description: Search for available flights
|
|
51
|
+
wab_action: searchFlights
|
|
52
|
+
params:
|
|
53
|
+
from: "{{from}}"
|
|
54
|
+
to: "{{to}}"
|
|
55
|
+
date: "{{date}}"
|
|
56
|
+
return_date: "{{return_date}}"
|
|
57
|
+
passengers: "{{passengers}}"
|
|
58
|
+
class: "{{class}}"
|
|
59
|
+
collect: true
|
|
60
|
+
|
|
61
|
+
- name: verify_fares
|
|
62
|
+
description: Verify displayed fares include all taxes and fees
|
|
63
|
+
wab_action: verifyPrice
|
|
64
|
+
require_pass: true
|
|
65
|
+
|
|
66
|
+
- name: check_total_price
|
|
67
|
+
description: Confirm total price matches breakdown
|
|
68
|
+
wab_action: verifyText
|
|
69
|
+
params:
|
|
70
|
+
fields: [base_fare, taxes, fees, total]
|
|
71
|
+
require_pass: true
|
|
72
|
+
|
|
73
|
+
- name: negotiate
|
|
74
|
+
description: Try to get a direct-booking discount
|
|
75
|
+
wab_action: negotiate
|
|
76
|
+
strategy: instant_payment
|
|
77
|
+
conditions:
|
|
78
|
+
proposed_discount: 3
|
|
79
|
+
fallback_strategy: off_peak
|
|
80
|
+
|
|
81
|
+
- name: book
|
|
82
|
+
description: Book the flight
|
|
83
|
+
wab_action: bookFlight
|
|
84
|
+
requires: [verify_fares, check_total_price]
|
|
85
|
+
|
|
86
|
+
fairness_rules:
|
|
87
|
+
prefer_local: false
|
|
88
|
+
prefer_small_business: false
|
|
89
|
+
currency: auto
|
|
90
|
+
min_reputation_score: 60
|
|
91
|
+
avoid_monopolies: false
|
|
92
|
+
price_transparency: true
|
|
93
|
+
|
|
94
|
+
negotiation:
|
|
95
|
+
enabled: true
|
|
96
|
+
max_rounds: 2
|
|
97
|
+
strategies:
|
|
98
|
+
- instant_payment
|
|
99
|
+
- off_peak
|
|
100
|
+
walk_away_threshold: 1
|
|
101
|
+
|
|
102
|
+
verification:
|
|
103
|
+
anti_hallucination: true
|
|
104
|
+
cross_check_prices: true
|
|
105
|
+
require_vision_match: true
|
|
106
|
+
max_price_variance: 0.02
|
|
107
|
+
verify_tax_inclusion: true
|
|
108
|
+
|
|
109
|
+
output:
|
|
110
|
+
format: table
|
|
111
|
+
include: [airline, flight_number, departure, arrival, duration, stops, fare, total_with_taxes, verification]
|
|
112
|
+
sort_by: total_with_taxes
|
|
113
|
+
limit: 20
|