web-agent-bridge 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/LICENSE +21 -21
  2. package/README.ar.md +446 -446
  3. package/README.md +780 -933
  4. package/bin/cli.js +80 -80
  5. package/bin/wab.js +80 -80
  6. package/examples/bidi-agent.js +119 -119
  7. package/examples/mcp-agent.js +94 -94
  8. package/examples/next-app-router/README.md +44 -0
  9. package/examples/puppeteer-agent.js +108 -108
  10. package/examples/saas-dashboard/README.md +55 -0
  11. package/examples/shopify-hydrogen/README.md +74 -0
  12. package/examples/vision-agent.js +171 -171
  13. package/examples/wordpress-elementor/README.md +77 -0
  14. package/package.json +69 -78
  15. package/public/.well-known/ai-assets.json +59 -0
  16. package/public/admin/login.html +84 -84
  17. package/public/ai.html +196 -0
  18. package/public/cookies.html +208 -208
  19. package/public/css/premium.css +317 -0
  20. package/public/css/styles.css +1235 -1235
  21. package/public/dashboard.html +704 -704
  22. package/public/demo.html +259 -0
  23. package/public/docs.html +585 -585
  24. package/public/feed.xml +89 -0
  25. package/public/index.html +495 -332
  26. package/public/js/auth-nav.js +31 -31
  27. package/public/js/auth-redirect.js +12 -12
  28. package/public/js/cookie-consent.js +56 -56
  29. package/public/js/wab-demo-page.js +721 -0
  30. package/public/js/ws-client.js +74 -74
  31. package/public/llms-full.txt +309 -0
  32. package/public/llms.txt +85 -0
  33. package/public/login.html +83 -83
  34. package/public/openapi.json +580 -0
  35. package/public/premium-dashboard.html +2487 -0
  36. package/public/premium.html +791 -0
  37. package/public/privacy.html +295 -295
  38. package/public/register.html +103 -103
  39. package/public/robots.txt +87 -0
  40. package/public/script/wab-consent.d.ts +36 -0
  41. package/public/script/wab-consent.js +104 -0
  42. package/public/script/wab-schema.js +131 -0
  43. package/public/script/wab.d.ts +108 -0
  44. package/public/script/wab.min.js +234 -0
  45. package/public/sitemap.xml +93 -0
  46. package/public/terms.html +254 -254
  47. package/public/video/tutorial.mp4 +0 -0
  48. package/script/ai-agent-bridge.js +1558 -1513
  49. package/sdk/README.md +55 -55
  50. package/sdk/index.d.ts +118 -0
  51. package/sdk/index.js +257 -203
  52. package/sdk/package.json +14 -14
  53. package/sdk/schema-discovery.js +83 -0
  54. package/server/config/secrets.js +94 -92
  55. package/server/index.js +0 -9
  56. package/server/middleware/adminAuth.js +30 -30
  57. package/server/middleware/auth.js +41 -41
  58. package/server/middleware/rateLimits.js +24 -24
  59. package/server/migrations/001_add_analytics_indexes.sql +7 -7
  60. package/server/migrations/002_premium_features.sql +418 -0
  61. package/server/models/adapters/index.js +33 -33
  62. package/server/models/adapters/mysql.js +183 -183
  63. package/server/models/adapters/postgresql.js +172 -172
  64. package/server/models/adapters/sqlite.js +7 -7
  65. package/server/models/db.js +561 -561
  66. package/server/routes/admin-premium.js +671 -0
  67. package/server/routes/admin.js +247 -247
  68. package/server/routes/api.js +131 -138
  69. package/server/routes/auth.js +51 -51
  70. package/server/routes/billing.js +45 -45
  71. package/server/routes/discovery.js +406 -329
  72. package/server/routes/license.js +240 -240
  73. package/server/routes/noscript.js +543 -543
  74. package/server/routes/premium-v2.js +686 -0
  75. package/server/routes/premium.js +724 -0
  76. package/server/routes/wab-api.js +476 -476
  77. package/server/services/agent-memory.js +625 -0
  78. package/server/services/email.js +204 -204
  79. package/server/services/fairness.js +420 -420
  80. package/server/services/plugins.js +747 -0
  81. package/server/services/premium.js +1883 -0
  82. package/server/services/self-healing.js +843 -0
  83. package/server/services/stripe.js +192 -192
  84. package/server/services/swarm.js +788 -0
  85. package/server/services/vision.js +871 -0
  86. package/server/utils/cache.js +125 -125
  87. package/server/utils/migrate.js +81 -81
  88. package/server/utils/secureFields.js +50 -50
  89. package/server/ws.js +101 -101
  90. package/docs/DEPLOY.md +0 -118
  91. package/docs/SPEC.md +0 -1540
  92. package/wab-mcp-adapter/README.md +0 -136
  93. package/wab-mcp-adapter/index.js +0 -555
  94. package/wab-mcp-adapter/package.json +0 -17
@@ -1,94 +1,94 @@
1
- /**
2
- * Example: Using WAB sites via the MCP adapter
3
- *
4
- * The WAB-MCP adapter exposes every WAB site action as an MCP tool,
5
- * so any MCP-compatible AI agent (Claude, GPT, etc.) can interact
6
- * with WAB-enabled websites through a uniform tool interface.
7
- *
8
- * Usage:
9
- * node examples/mcp-agent.js <site-url>
10
- */
11
-
12
- const { WABMCPAdapter } = require('../wab-mcp-adapter');
13
-
14
- const TARGET = process.argv[2] || 'http://localhost:3000';
15
-
16
- async function main() {
17
- console.log('\n WAB → MCP Adapter Demo');
18
- console.log(` Target: ${TARGET}\n`);
19
-
20
- const adapter = new WABMCPAdapter({
21
- siteUrl: TARGET,
22
- transport: 'http'
23
- });
24
-
25
- // Step 1: Discover site capabilities
26
- console.log('1. Discovering WAB capabilities...');
27
- const discovery = await adapter.discover();
28
- if (discovery) {
29
- console.log(` Provider: ${discovery.provider?.name || 'unknown'}`);
30
- console.log(` Domain: ${discovery.provider?.domain || 'unknown'}`);
31
- console.log(` Tier: ${discovery.capabilities?.tier || 'free'}`);
32
- console.log(` Features: ${(discovery.capabilities?.features || []).join(', ')}`);
33
- if (discovery.fairness) {
34
- console.log(` Fairness: score=${discovery.fairness.neutrality_score}, independent=${discovery.fairness.is_independent}`);
35
- }
36
- } else {
37
- console.log(' No discovery document found (site may not have WAB configured)');
38
- }
39
-
40
- // Step 2: List MCP tools
41
- console.log('\n2. Available MCP tools:');
42
- const tools = await adapter.getTools();
43
- tools.forEach(tool => {
44
- const params = tool.input_schema?.properties
45
- ? Object.keys(tool.input_schema.properties).join(', ')
46
- : 'none';
47
- console.log(` - ${tool.name}: ${tool.description} (params: ${params})`);
48
- });
49
-
50
- // Step 3: Execute built-in tools
51
- console.log('\n3. Executing wab_get_page_info...');
52
- try {
53
- const infoResult = await adapter.executeTool('wab_get_page_info', {});
54
- const info = infoResult.content;
55
- if (infoResult.is_error) {
56
- console.log(` Error: ${info.error}`);
57
- } else {
58
- console.log(` Title: ${info.title || 'N/A'}`);
59
- console.log(` Version: ${info.bridgeVersion || 'N/A'}`);
60
- console.log(` Domain: ${info.domain || 'N/A'}`);
61
- }
62
- } catch (err) {
63
- console.log(` Error: ${err.message}`);
64
- }
65
-
66
- // Step 4: Search the fairness registry
67
- console.log('\n4. Fairness-weighted search (demo):');
68
- try {
69
- const searchResult = await adapter.executeTool('wab_fairness_search', {
70
- query: 'e-commerce',
71
- limit: 5
72
- });
73
- const search = searchResult.content;
74
- if (searchResult.is_error) {
75
- console.log(` Error: ${search.error}`);
76
- } else if (search.results?.length) {
77
- search.results.forEach(r => {
78
- console.log(` - ${r.name} (${r.domain}) — score: ${r.final_score}`);
79
- });
80
- } else {
81
- console.log(' No sites registered in directory yet.');
82
- }
83
- } catch (err) {
84
- console.log(` Registry not available: ${err.message}`);
85
- }
86
-
87
- console.log('\nDone. In a real MCP integration, these tools would be');
88
- console.log('exposed to Claude/GPT via the MCP tool-use protocol.\n');
89
- }
90
-
91
- main().catch(err => {
92
- console.error('Error:', err.message);
93
- process.exit(1);
94
- });
1
+ /**
2
+ * Example: Using WAB sites via the MCP adapter
3
+ *
4
+ * The WAB-MCP adapter exposes every WAB site action as an MCP tool,
5
+ * so any MCP-compatible AI agent (Claude, GPT, etc.) can interact
6
+ * with WAB-enabled websites through a uniform tool interface.
7
+ *
8
+ * Usage:
9
+ * node examples/mcp-agent.js <site-url>
10
+ */
11
+
12
+ const { WABMCPAdapter } = require('../wab-mcp-adapter');
13
+
14
+ const TARGET = process.argv[2] || 'http://localhost:3000';
15
+
16
+ async function main() {
17
+ console.log('\n WAB → MCP Adapter Demo');
18
+ console.log(` Target: ${TARGET}\n`);
19
+
20
+ const adapter = new WABMCPAdapter({
21
+ siteUrl: TARGET,
22
+ transport: 'http'
23
+ });
24
+
25
+ // Step 1: Discover site capabilities
26
+ console.log('1. Discovering WAB capabilities...');
27
+ const discovery = await adapter.discover();
28
+ if (discovery) {
29
+ console.log(` Provider: ${discovery.provider?.name || 'unknown'}`);
30
+ console.log(` Domain: ${discovery.provider?.domain || 'unknown'}`);
31
+ console.log(` Tier: ${discovery.capabilities?.tier || 'free'}`);
32
+ console.log(` Features: ${(discovery.capabilities?.features || []).join(', ')}`);
33
+ if (discovery.fairness) {
34
+ console.log(` Fairness: score=${discovery.fairness.neutrality_score}, independent=${discovery.fairness.is_independent}`);
35
+ }
36
+ } else {
37
+ console.log(' No discovery document found (site may not have WAB configured)');
38
+ }
39
+
40
+ // Step 2: List MCP tools
41
+ console.log('\n2. Available MCP tools:');
42
+ const tools = await adapter.getTools();
43
+ tools.forEach(tool => {
44
+ const params = tool.input_schema?.properties
45
+ ? Object.keys(tool.input_schema.properties).join(', ')
46
+ : 'none';
47
+ console.log(` - ${tool.name}: ${tool.description} (params: ${params})`);
48
+ });
49
+
50
+ // Step 3: Execute built-in tools
51
+ console.log('\n3. Executing wab_get_page_info...');
52
+ try {
53
+ const infoResult = await adapter.executeTool('wab_get_page_info', {});
54
+ const info = infoResult.content;
55
+ if (infoResult.is_error) {
56
+ console.log(` Error: ${info.error}`);
57
+ } else {
58
+ console.log(` Title: ${info.title || 'N/A'}`);
59
+ console.log(` Version: ${info.bridgeVersion || 'N/A'}`);
60
+ console.log(` Domain: ${info.domain || 'N/A'}`);
61
+ }
62
+ } catch (err) {
63
+ console.log(` Error: ${err.message}`);
64
+ }
65
+
66
+ // Step 4: Search the fairness registry
67
+ console.log('\n4. Fairness-weighted search (demo):');
68
+ try {
69
+ const searchResult = await adapter.executeTool('wab_fairness_search', {
70
+ query: 'e-commerce',
71
+ limit: 5
72
+ });
73
+ const search = searchResult.content;
74
+ if (searchResult.is_error) {
75
+ console.log(` Error: ${search.error}`);
76
+ } else if (search.results?.length) {
77
+ search.results.forEach(r => {
78
+ console.log(` - ${r.name} (${r.domain}) — score: ${r.final_score}`);
79
+ });
80
+ } else {
81
+ console.log(' No sites registered in directory yet.');
82
+ }
83
+ } catch (err) {
84
+ console.log(` Registry not available: ${err.message}`);
85
+ }
86
+
87
+ console.log('\nDone. In a real MCP integration, these tools would be');
88
+ console.log('exposed to Claude/GPT via the MCP tool-use protocol.\n');
89
+ }
90
+
91
+ main().catch(err => {
92
+ console.error('Error:', err.message);
93
+ process.exit(1);
94
+ });
@@ -0,0 +1,44 @@
1
+ # Next.js App Router + WAB
2
+
3
+ 1. Install the React helpers (from repo root after publish, or `file:` link):
4
+
5
+ ```bash
6
+ npm install @web-agent-bridge/react
7
+ ```
8
+
9
+ 2. Add a client component `components/WabShop.tsx`:
10
+
11
+ ```tsx
12
+ 'use client';
13
+
14
+ import { WABProvider, useWAB } from '@web-agent-bridge/react';
15
+ import { useEffect } from 'react';
16
+
17
+ function WabInner() {
18
+ const { ready, discover, execute } = useWAB({
19
+ name: 'My App',
20
+ actions: {
21
+ ping: { description: 'Health check', run: () => ({ ok: true }) },
22
+ },
23
+ });
24
+
25
+ useEffect(() => {
26
+ if (!ready) return;
27
+ discover().then(console.log);
28
+ }, [ready, discover]);
29
+
30
+ return <p>WAB {ready ? 'ready' : 'loading…'}</p>;
31
+ }
32
+
33
+ export default function WabShop() {
34
+ return (
35
+ <WABProvider scriptSrc="https://webagentbridge.com/script/wab.min.js">
36
+ <WabInner />
37
+ </WABProvider>
38
+ );
39
+ }
40
+ ```
41
+
42
+ 3. Import `WabShop` from any `app/.../page.tsx` (server component can import client components).
43
+
44
+ Use `next/script` with `strategy="beforeInteractive"` if you prefer loading `wab.min.js` in `layout.tsx` instead of `WABProvider`.
@@ -1,108 +1,108 @@
1
- /**
2
- * Example: Basic AI Agent using Puppeteer + WAB
3
- *
4
- * This agent connects to a website that has the Web Agent Bridge script installed,
5
- * discovers available actions, and executes them.
6
- *
7
- * Prerequisites:
8
- * npm install puppeteer
9
- *
10
- * Usage:
11
- * node examples/puppeteer-agent.js <url>
12
- */
13
-
14
- const puppeteer = require('puppeteer');
15
-
16
- const TARGET_URL = process.argv[2] || 'http://localhost:3000';
17
-
18
- async function main() {
19
- console.log(`\n🤖 WAB Puppeteer Agent`);
20
- console.log(` Target: ${TARGET_URL}\n`);
21
-
22
- const browser = await puppeteer.launch({ headless: true });
23
- const page = await browser.newPage();
24
-
25
- // Navigate to the target site
26
- await page.goto(TARGET_URL, { waitUntil: 'networkidle2' });
27
- console.log(`✓ Page loaded: ${await page.title()}`);
28
-
29
- // Wait for WAB bridge to be ready
30
- const hasBridge = await page.evaluate(() => {
31
- return new Promise((resolve) => {
32
- if (window.AICommands && window.AICommands._ready) {
33
- resolve(true);
34
- } else {
35
- // Wait up to 5 seconds for bridge
36
- const timeout = setTimeout(() => resolve(false), 5000);
37
- document.addEventListener('wab:ready', () => {
38
- clearTimeout(timeout);
39
- resolve(true);
40
- });
41
- }
42
- });
43
- });
44
-
45
- if (!hasBridge) {
46
- console.log('✗ Web Agent Bridge not found on this page.');
47
- await browser.close();
48
- return;
49
- }
50
-
51
- console.log('✓ Web Agent Bridge detected\n');
52
-
53
- // Step 1: Get page info
54
- const pageInfo = await page.evaluate(() => window.AICommands.getPageInfo());
55
- console.log('📄 Page Info:');
56
- console.log(` Title: ${pageInfo.title}`);
57
- console.log(` URL: ${pageInfo.url}`);
58
- console.log(` Bridge Version: ${pageInfo.bridgeVersion}`);
59
- console.log(` Tier: ${pageInfo.tier}`);
60
- console.log(` Actions Available: ${pageInfo.actionsCount}`);
61
- console.log(` Rate Limit Remaining: ${pageInfo.rateLimitRemaining}\n`);
62
-
63
- // Step 2: Discover all available actions
64
- const actions = await page.evaluate(() => window.AICommands.getActions());
65
- console.log(`🔍 Discovered ${actions.length} actions:`);
66
- actions.forEach((action) => {
67
- console.log(` • ${action.name} (${action.trigger}) — ${action.description}`);
68
- if (action.fields) {
69
- action.fields.forEach((f) => {
70
- console.log(` └─ ${f.name}: ${f.type}${f.required ? ' (required)' : ''}`);
71
- });
72
- }
73
- });
74
-
75
- // Step 3: Read content from the page
76
- const content = await page.evaluate(() => {
77
- return window.AICommands.readContent('h1') || window.AICommands.readContent('title');
78
- });
79
- if (content && content.success) {
80
- console.log(`\n📖 Page heading: "${content.text}"`);
81
- }
82
-
83
- // Step 4: Execute a click action (first available)
84
- const clickAction = actions.find((a) => a.trigger === 'click');
85
- if (clickAction) {
86
- console.log(`\n▶ Executing action: "${clickAction.name}"`);
87
- const result = await page.evaluate(
88
- (name) => window.AICommands.execute(name),
89
- clickAction.name
90
- );
91
- console.log(` Result: ${result.success ? '✓ Success' : '✗ ' + result.error}`);
92
- }
93
-
94
- // Step 5: List permissions
95
- const permissions = pageInfo.permissions;
96
- console.log('\n🔐 Permissions:');
97
- Object.entries(permissions).forEach(([key, value]) => {
98
- console.log(` ${value ? '✓' : '✗'} ${key}`);
99
- });
100
-
101
- console.log('\n✓ Agent session complete.');
102
- await browser.close();
103
- }
104
-
105
- main().catch((err) => {
106
- console.error('Agent error:', err.message);
107
- process.exit(1);
108
- });
1
+ /**
2
+ * Example: Basic AI Agent using Puppeteer + WAB
3
+ *
4
+ * This agent connects to a website that has the Web Agent Bridge script installed,
5
+ * discovers available actions, and executes them.
6
+ *
7
+ * Prerequisites:
8
+ * npm install puppeteer
9
+ *
10
+ * Usage:
11
+ * node examples/puppeteer-agent.js <url>
12
+ */
13
+
14
+ const puppeteer = require('puppeteer');
15
+
16
+ const TARGET_URL = process.argv[2] || 'http://localhost:3000';
17
+
18
+ async function main() {
19
+ console.log(`\n🤖 WAB Puppeteer Agent`);
20
+ console.log(` Target: ${TARGET_URL}\n`);
21
+
22
+ const browser = await puppeteer.launch({ headless: true });
23
+ const page = await browser.newPage();
24
+
25
+ // Navigate to the target site
26
+ await page.goto(TARGET_URL, { waitUntil: 'networkidle2' });
27
+ console.log(`✓ Page loaded: ${await page.title()}`);
28
+
29
+ // Wait for WAB bridge to be ready
30
+ const hasBridge = await page.evaluate(() => {
31
+ return new Promise((resolve) => {
32
+ if (window.AICommands && window.AICommands._ready) {
33
+ resolve(true);
34
+ } else {
35
+ // Wait up to 5 seconds for bridge
36
+ const timeout = setTimeout(() => resolve(false), 5000);
37
+ document.addEventListener('wab:ready', () => {
38
+ clearTimeout(timeout);
39
+ resolve(true);
40
+ });
41
+ }
42
+ });
43
+ });
44
+
45
+ if (!hasBridge) {
46
+ console.log('✗ Web Agent Bridge not found on this page.');
47
+ await browser.close();
48
+ return;
49
+ }
50
+
51
+ console.log('✓ Web Agent Bridge detected\n');
52
+
53
+ // Step 1: Get page info
54
+ const pageInfo = await page.evaluate(() => window.AICommands.getPageInfo());
55
+ console.log('📄 Page Info:');
56
+ console.log(` Title: ${pageInfo.title}`);
57
+ console.log(` URL: ${pageInfo.url}`);
58
+ console.log(` Bridge Version: ${pageInfo.bridgeVersion}`);
59
+ console.log(` Tier: ${pageInfo.tier}`);
60
+ console.log(` Actions Available: ${pageInfo.actionsCount}`);
61
+ console.log(` Rate Limit Remaining: ${pageInfo.rateLimitRemaining}\n`);
62
+
63
+ // Step 2: Discover all available actions
64
+ const actions = await page.evaluate(() => window.AICommands.getActions());
65
+ console.log(`🔍 Discovered ${actions.length} actions:`);
66
+ actions.forEach((action) => {
67
+ console.log(` • ${action.name} (${action.trigger}) — ${action.description}`);
68
+ if (action.fields) {
69
+ action.fields.forEach((f) => {
70
+ console.log(` └─ ${f.name}: ${f.type}${f.required ? ' (required)' : ''}`);
71
+ });
72
+ }
73
+ });
74
+
75
+ // Step 3: Read content from the page
76
+ const content = await page.evaluate(() => {
77
+ return window.AICommands.readContent('h1') || window.AICommands.readContent('title');
78
+ });
79
+ if (content && content.success) {
80
+ console.log(`\n📖 Page heading: "${content.text}"`);
81
+ }
82
+
83
+ // Step 4: Execute a click action (first available)
84
+ const clickAction = actions.find((a) => a.trigger === 'click');
85
+ if (clickAction) {
86
+ console.log(`\n▶ Executing action: "${clickAction.name}"`);
87
+ const result = await page.evaluate(
88
+ (name) => window.AICommands.execute(name),
89
+ clickAction.name
90
+ );
91
+ console.log(` Result: ${result.success ? '✓ Success' : '✗ ' + result.error}`);
92
+ }
93
+
94
+ // Step 5: List permissions
95
+ const permissions = pageInfo.permissions;
96
+ console.log('\n🔐 Permissions:');
97
+ Object.entries(permissions).forEach(([key, value]) => {
98
+ console.log(` ${value ? '✓' : '✗'} ${key}`);
99
+ });
100
+
101
+ console.log('\n✓ Agent session complete.');
102
+ await browser.close();
103
+ }
104
+
105
+ main().catch((err) => {
106
+ console.error('Agent error:', err.message);
107
+ process.exit(1);
108
+ });
@@ -0,0 +1,55 @@
1
+ # SaaS Dashboard Example (Notion-style)
2
+
3
+ A practical setup for internal dashboards where agents read KPIs and trigger safe workflows.
4
+
5
+ ## Embed
6
+
7
+ ```html
8
+ <script src="https://webagentbridge.com/script/wab.min.js"></script>
9
+ <script>
10
+ window.WAB.init({
11
+ name: 'Acme SaaS Dashboard',
12
+ actions: {
13
+ getKpiCards: {
14
+ description: 'Return KPI card values from dashboard widgets',
15
+ run: function () {
16
+ var cards = Array.from(document.querySelectorAll('[data-kpi-card]')).map(function (card) {
17
+ return {
18
+ key: card.getAttribute('data-kpi-card'),
19
+ label: (card.querySelector('[data-kpi-label]') || {}).textContent || null,
20
+ value: (card.querySelector('[data-kpi-value]') || {}).textContent || null
21
+ };
22
+ });
23
+ return { success: true, cards: cards };
24
+ }
25
+ },
26
+ openCustomerById: {
27
+ description: 'Open customer panel using data-customer-id selector',
28
+ params: [{ name: 'customerId', type: 'string', required: true }],
29
+ run: function (params) {
30
+ var id = String(params.customerId || '').trim();
31
+ if (!id) return { success: false, error: 'customerId is required' };
32
+ var row = document.querySelector('[data-customer-id="' + CSS.escape(id) + '"]');
33
+ if (!row) return { success: false, error: 'Customer not found' };
34
+ row.click();
35
+ return { success: true, customerId: id };
36
+ }
37
+ },
38
+ triggerInvoiceReminder: {
39
+ description: 'Trigger existing invoice reminder button from dashboard row',
40
+ params: [{ name: 'invoiceId', type: 'string', required: true }],
41
+ run: function (params) {
42
+ var id = String(params.invoiceId || '').trim();
43
+ if (!id) return { success: false, error: 'invoiceId is required' };
44
+ var btn = document.querySelector('[data-invoice-id="' + CSS.escape(id) + '"] [data-action="send-reminder"]');
45
+ if (!btn) return { success: false, error: 'Reminder action not found for invoice' };
46
+ btn.click();
47
+ return { success: true, invoiceId: id };
48
+ }
49
+ }
50
+ }
51
+ });
52
+ </script>
53
+ ```
54
+
55
+ The selectors use data attributes so actions stay stable across UI redesigns.
@@ -0,0 +1,74 @@
1
+ # Shopify Hydrogen + WAB
2
+
3
+ This example wires WAB into a Hydrogen storefront client component and exposes practical commerce actions.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @web-agent-bridge/react
9
+ ```
10
+
11
+ ## Component
12
+
13
+ Create `app/components/WabHydrogenBridge.tsx`:
14
+
15
+ ```tsx
16
+ 'use client';
17
+
18
+ import { useEffect } from 'react';
19
+ import { WABProvider, useWAB, useWABAction } from '@web-agent-bridge/react';
20
+
21
+ function BridgeInner() {
22
+ const { ready, discover, instance } = useWAB({
23
+ name: 'Hydrogen Storefront',
24
+ actions: {
25
+ getCartCount: {
26
+ description: 'Return cart item count from cart badge',
27
+ run: () => {
28
+ const badge = document.querySelector('[data-cart-count], .cart-count');
29
+ const value = Number((badge?.textContent || '0').trim()) || 0;
30
+ return { count: value };
31
+ }
32
+ },
33
+ addFirstVisibleProductToCart: {
34
+ description: 'Click the first visible add-to-cart button on the page',
35
+ run: () => {
36
+ const btn = Array.from(document.querySelectorAll('button, a')).find((el) =>
37
+ /add\s*to\s*cart/i.test((el.textContent || '').trim())
38
+ );
39
+ if (!btn) return { success: false, error: 'No add-to-cart button found' };
40
+ (btn as HTMLElement).click();
41
+ return { success: true };
42
+ }
43
+ }
44
+ }
45
+ });
46
+
47
+ const { run: runGetCartCount, result: cartCount } = useWABAction<{ count: number }>('getCartCount', {
48
+ instance
49
+ });
50
+
51
+ useEffect(() => {
52
+ if (!ready) return;
53
+ discover().then((doc) => console.log('WAB discover:', doc));
54
+ runGetCartCount().catch(() => {});
55
+ }, [ready, discover, runGetCartCount]);
56
+
57
+ return (
58
+ <div>
59
+ <p>WAB status: {ready ? 'ready' : 'loading'}</p>
60
+ <p>Cart count: {cartCount?.count ?? 0}</p>
61
+ </div>
62
+ );
63
+ }
64
+
65
+ export default function WabHydrogenBridge() {
66
+ return (
67
+ <WABProvider scriptSrc="https://webagentbridge.com/script/wab.min.js">
68
+ <BridgeInner />
69
+ </WABProvider>
70
+ );
71
+ }
72
+ ```
73
+
74
+ Import this component inside any Hydrogen page so WAB is available to agents.