@teneo-protocol/sdk 1.0.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 (211) hide show
  1. package/.github/workflows/publish-npm.yml +8 -6
  2. package/CHANGELOG.md +265 -0
  3. package/README.md +406 -53
  4. package/dist/core/websocket-client.d.ts +13 -0
  5. package/dist/core/websocket-client.d.ts.map +1 -1
  6. package/dist/core/websocket-client.js +34 -3
  7. package/dist/core/websocket-client.js.map +1 -1
  8. package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts +76 -0
  9. package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts.map +1 -0
  10. package/dist/handlers/message-handlers/agent-room-operation-response-handler.js +70 -0
  11. package/dist/handlers/message-handlers/agent-room-operation-response-handler.js.map +1 -0
  12. package/dist/handlers/message-handlers/agent-selected-handler.d.ts +92 -38
  13. package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -1
  14. package/dist/handlers/message-handlers/agent-status-update-handler.d.ts +904 -0
  15. package/dist/handlers/message-handlers/agent-status-update-handler.d.ts.map +1 -0
  16. package/dist/handlers/message-handlers/agent-status-update-handler.js +51 -0
  17. package/dist/handlers/message-handlers/agent-status-update-handler.js.map +1 -0
  18. package/dist/handlers/message-handlers/auth-error-handler.d.ts +45 -31
  19. package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -1
  20. package/dist/handlers/message-handlers/auth-message-handler.d.ts +6 -0
  21. package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -1
  22. package/dist/handlers/message-handlers/auth-message-handler.js +65 -5
  23. package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -1
  24. package/dist/handlers/message-handlers/auth-required-handler.d.ts +49 -31
  25. package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -1
  26. package/dist/handlers/message-handlers/auth-success-handler.d.ts +6 -0
  27. package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -1
  28. package/dist/handlers/message-handlers/auth-success-handler.js +46 -4
  29. package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -1
  30. package/dist/handlers/message-handlers/challenge-handler.d.ts +45 -31
  31. package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -1
  32. package/dist/handlers/message-handlers/error-message-handler.d.ts +49 -31
  33. package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -1
  34. package/dist/handlers/message-handlers/index.d.ts +5 -0
  35. package/dist/handlers/message-handlers/index.d.ts.map +1 -1
  36. package/dist/handlers/message-handlers/index.js +23 -1
  37. package/dist/handlers/message-handlers/index.js.map +1 -1
  38. package/dist/handlers/message-handlers/list-available-agents-handler.d.ts +877 -0
  39. package/dist/handlers/message-handlers/list-available-agents-handler.d.ts.map +1 -0
  40. package/dist/handlers/message-handlers/list-available-agents-handler.js +38 -0
  41. package/dist/handlers/message-handlers/list-available-agents-handler.js.map +1 -0
  42. package/dist/handlers/message-handlers/list-room-agents-handler.d.ts +886 -0
  43. package/dist/handlers/message-handlers/list-room-agents-handler.d.ts.map +1 -0
  44. package/dist/handlers/message-handlers/list-room-agents-handler.js +51 -0
  45. package/dist/handlers/message-handlers/list-room-agents-handler.js.map +1 -0
  46. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +178 -89
  47. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -1
  48. package/dist/handlers/message-handlers/ping-pong-handler.d.ts +62 -58
  49. package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -1
  50. package/dist/handlers/message-handlers/regular-message-handler.d.ts +31 -29
  51. package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -1
  52. package/dist/handlers/message-handlers/regular-message-handler.js +1 -0
  53. package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -1
  54. package/dist/handlers/message-handlers/room-operation-response-handler.d.ts +328 -0
  55. package/dist/handlers/message-handlers/room-operation-response-handler.d.ts.map +1 -0
  56. package/dist/handlers/message-handlers/room-operation-response-handler.js +92 -0
  57. package/dist/handlers/message-handlers/room-operation-response-handler.js.map +1 -0
  58. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +53 -31
  59. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -1
  60. package/dist/handlers/message-handlers/types.d.ts +2 -0
  61. package/dist/handlers/message-handlers/types.d.ts.map +1 -1
  62. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +53 -31
  63. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -1
  64. package/dist/managers/agent-room-manager.d.ts +222 -0
  65. package/dist/managers/agent-room-manager.d.ts.map +1 -0
  66. package/dist/managers/agent-room-manager.js +508 -0
  67. package/dist/managers/agent-room-manager.js.map +1 -0
  68. package/dist/managers/index.d.ts +2 -0
  69. package/dist/managers/index.d.ts.map +1 -1
  70. package/dist/managers/index.js +5 -1
  71. package/dist/managers/index.js.map +1 -1
  72. package/dist/managers/message-router.d.ts +1 -1
  73. package/dist/managers/message-router.d.ts.map +1 -1
  74. package/dist/managers/message-router.js +41 -4
  75. package/dist/managers/message-router.js.map +1 -1
  76. package/dist/managers/room-management-manager.d.ts +213 -0
  77. package/dist/managers/room-management-manager.d.ts.map +1 -0
  78. package/dist/managers/room-management-manager.js +440 -0
  79. package/dist/managers/room-management-manager.js.map +1 -0
  80. package/dist/managers/room-manager.d.ts +4 -4
  81. package/dist/managers/room-manager.d.ts.map +1 -1
  82. package/dist/managers/room-manager.js +1 -1
  83. package/dist/managers/room-manager.js.map +1 -1
  84. package/dist/teneo-sdk.d.ts +362 -14
  85. package/dist/teneo-sdk.d.ts.map +1 -1
  86. package/dist/teneo-sdk.js +497 -7
  87. package/dist/teneo-sdk.js.map +1 -1
  88. package/dist/types/config.d.ts +63 -54
  89. package/dist/types/config.d.ts.map +1 -1
  90. package/dist/types/config.js +9 -5
  91. package/dist/types/config.js.map +1 -1
  92. package/dist/types/error-codes.d.ts +2 -0
  93. package/dist/types/error-codes.d.ts.map +1 -1
  94. package/dist/types/error-codes.js +3 -0
  95. package/dist/types/error-codes.js.map +1 -1
  96. package/dist/types/events.d.ts +132 -68
  97. package/dist/types/events.d.ts.map +1 -1
  98. package/dist/types/events.js.map +1 -1
  99. package/dist/types/index.d.ts +1 -1
  100. package/dist/types/index.d.ts.map +1 -1
  101. package/dist/types/index.js +27 -2
  102. package/dist/types/index.js.map +1 -1
  103. package/dist/types/messages.d.ts +11396 -2559
  104. package/dist/types/messages.d.ts.map +1 -1
  105. package/dist/types/messages.js +294 -27
  106. package/dist/types/messages.js.map +1 -1
  107. package/dist/types/validation.d.ts.map +1 -1
  108. package/dist/types/validation.js +1 -1
  109. package/dist/types/validation.js.map +1 -1
  110. package/dist/utils/bounded-queue.d.ts +1 -1
  111. package/dist/utils/bounded-queue.js +6 -6
  112. package/dist/utils/circuit-breaker.d.ts.map +1 -1
  113. package/dist/utils/circuit-breaker.js.map +1 -1
  114. package/dist/utils/event-waiter.d.ts.map +1 -1
  115. package/dist/utils/event-waiter.js +2 -1
  116. package/dist/utils/event-waiter.js.map +1 -1
  117. package/dist/utils/rate-limiter.d.ts.map +1 -1
  118. package/dist/utils/rate-limiter.js +4 -6
  119. package/dist/utils/rate-limiter.js.map +1 -1
  120. package/dist/utils/secure-private-key.d.ts.map +1 -1
  121. package/dist/utils/secure-private-key.js +9 -15
  122. package/dist/utils/secure-private-key.js.map +1 -1
  123. package/dist/utils/signature-verifier.d.ts +2 -2
  124. package/dist/utils/signature-verifier.d.ts.map +1 -1
  125. package/dist/utils/signature-verifier.js +5 -5
  126. package/dist/utils/signature-verifier.js.map +1 -1
  127. package/examples/.env.example +1 -1
  128. package/examples/agent-room-management-example.ts +334 -0
  129. package/examples/claude-agent-x-follower/.env.example +117 -0
  130. package/examples/claude-agent-x-follower/QUICKSTART.md +243 -0
  131. package/examples/claude-agent-x-follower/README.md +540 -0
  132. package/examples/claude-agent-x-follower/index.ts +248 -0
  133. package/examples/claude-agent-x-follower/package.json +37 -0
  134. package/examples/claude-agent-x-follower/tsconfig.json +20 -0
  135. package/examples/n8n-teneo/.env.example +127 -0
  136. package/examples/n8n-teneo/Dockerfile +42 -0
  137. package/examples/n8n-teneo/README.md +564 -0
  138. package/examples/n8n-teneo/docker-compose.yml +71 -0
  139. package/examples/n8n-teneo/index.ts +177 -0
  140. package/examples/n8n-teneo/package.json +22 -0
  141. package/examples/n8n-teneo/tsconfig.json +12 -0
  142. package/examples/n8n-teneo/workflows/x-timeline.json +66 -0
  143. package/examples/openai-teneo/.env.example +130 -0
  144. package/examples/openai-teneo/README.md +635 -0
  145. package/examples/openai-teneo/index.ts +280 -0
  146. package/examples/openai-teneo/package.json +24 -0
  147. package/examples/openai-teneo/tsconfig.json +16 -0
  148. package/examples/production-dashboard/.env.example +5 -3
  149. package/examples/production-dashboard/README.md +839 -0
  150. package/examples/production-dashboard/pnpm-lock.yaml +92 -0
  151. package/examples/production-dashboard/public/dashboard.html +1150 -504
  152. package/examples/production-dashboard/server.ts +428 -12
  153. package/examples/room-management-example.ts +285 -0
  154. package/examples/usage/.env.example +17 -0
  155. package/examples/usage/01-connect.ts +116 -0
  156. package/examples/usage/02-list-agents.ts +153 -0
  157. package/examples/usage/03-pick-agent.ts +201 -0
  158. package/examples/usage/04-find-by-capability.ts +237 -0
  159. package/examples/usage/05-webhook-example.ts +319 -0
  160. package/examples/usage/06-simple-api-server.ts +396 -0
  161. package/examples/usage/07-event-listener.ts +402 -0
  162. package/examples/usage/README.md +383 -0
  163. package/examples/usage/package.json +42 -0
  164. package/package.json +13 -3
  165. package/src/core/websocket-client.ts +43 -9
  166. package/src/formatters/response-formatter.test.ts +8 -2
  167. package/src/handlers/message-handlers/agent-room-operation-response-handler.ts +83 -0
  168. package/src/handlers/message-handlers/agent-status-update-handler.ts +58 -0
  169. package/src/handlers/message-handlers/auth-message-handler.ts +73 -5
  170. package/src/handlers/message-handlers/auth-success-handler.ts +58 -6
  171. package/src/handlers/message-handlers/index.ts +19 -0
  172. package/src/handlers/message-handlers/list-available-agents-handler.ts +41 -0
  173. package/src/handlers/message-handlers/list-room-agents-handler.ts +61 -0
  174. package/src/handlers/message-handlers/regular-message-handler.ts +1 -0
  175. package/src/handlers/message-handlers/room-operation-response-handler.ts +105 -0
  176. package/src/handlers/message-handlers/types.ts +6 -0
  177. package/src/handlers/webhook-handler.test.ts +13 -10
  178. package/src/managers/agent-room-manager.ts +609 -0
  179. package/src/managers/index.ts +2 -0
  180. package/src/managers/message-router.ts +48 -6
  181. package/src/managers/room-management-manager.ts +523 -0
  182. package/src/managers/room-manager.ts +12 -6
  183. package/src/teneo-sdk.ts +543 -10
  184. package/src/types/config.ts +13 -6
  185. package/src/types/error-codes.ts +4 -0
  186. package/src/types/events.ts +24 -0
  187. package/src/types/index.ts +55 -0
  188. package/src/types/messages.ts +374 -41
  189. package/src/types/validation.ts +4 -1
  190. package/src/utils/bounded-queue.ts +9 -9
  191. package/src/utils/circuit-breaker.ts +4 -1
  192. package/src/utils/deduplication-cache.test.ts +2 -6
  193. package/src/utils/event-waiter.test.ts +4 -1
  194. package/src/utils/event-waiter.ts +5 -7
  195. package/src/utils/rate-limiter.test.ts +5 -17
  196. package/src/utils/rate-limiter.ts +6 -9
  197. package/src/utils/secure-private-key.test.ts +66 -59
  198. package/src/utils/secure-private-key.ts +10 -16
  199. package/src/utils/signature-verifier.test.ts +75 -70
  200. package/src/utils/signature-verifier.ts +7 -8
  201. package/src/utils/ssrf-validator.test.ts +3 -3
  202. package/tests/integration/room-management.test.ts +514 -0
  203. package/tests/integration/websocket.test.ts +1 -1
  204. package/tests/unit/handlers/agent-room-operation-response-handler.test.ts +394 -0
  205. package/tests/unit/handlers/agent-status-update-handler.test.ts +407 -0
  206. package/tests/unit/handlers/auth-success-handler-rooms.test.ts +699 -0
  207. package/tests/unit/handlers/list-available-agents-handler.test.ts +256 -0
  208. package/tests/unit/handlers/list-room-agents-handler.test.ts +294 -0
  209. package/tests/unit/handlers/room-operation-response-handler.test.ts +527 -0
  210. package/tests/unit/managers/agent-room-manager.test.ts +534 -0
  211. package/tests/unit/managers/room-management-manager.test.ts +438 -0
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Example 5: Webhook Integration (Production Pattern)
3
+ *
4
+ * This example demonstrates production-ready webhook integration
5
+ * following the pattern from examples/production-dashboard:
6
+ *
7
+ * - Setting up a local webhook receiver with multiple endpoints
8
+ * - Configuring SDK with allowInsecureWebhooks for local development
9
+ * - Using sdk.configureWebhook() for runtime webhook setup
10
+ * - Receiving and processing different webhook event types
11
+ * - Webhook retry logic and error handling
12
+ * - Circuit breaker behavior on failures
13
+ * - Tracking webhook statistics and metrics
14
+ *
15
+ * The webhook server provides:
16
+ * - POST /webhook - Receives webhook events from SDK
17
+ * - GET /health - Health check with metrics
18
+ * - GET /webhooks - List all received webhooks
19
+ *
20
+ * Run: npx tsx examples/usage/05-webhook-example.ts
21
+ */
22
+
23
+ import "dotenv/config";
24
+ import { TeneoSDK, SDKConfigBuilder } from "../../dist/index.js";
25
+ import express from "express";
26
+ import type { Request, Response } from "express";
27
+
28
+ // Load configuration from environment
29
+ const WS_URL =
30
+ process.env.WS_URL || "wss://your-teneo-server.com/ws";
31
+ const PRIVATE_KEY = process.env.PRIVATE_KEY || "";
32
+ const DEFAULT_ROOM = process.env.DEFAULT_ROOM || "general";
33
+ const WEBHOOK_PORT = parseInt(process.env.WEBHOOK_PORT || "3001");
34
+ const WEBHOOK_SINK_PORT = parseInt(process.env.WEBHOOK_SINK_PORT || "3000");
35
+
36
+ // Track received webhooks for stats
37
+ let webhookCounter = 0;
38
+ const receivedWebhooks: any[] = [];
39
+
40
+ async function main() {
41
+ console.log("šŸš€ Example 5: Webhook Integration\n");
42
+
43
+ if (!PRIVATE_KEY) {
44
+ console.error("āŒ ERROR: PRIVATE_KEY environment variable is required\n");
45
+ process.exit(1);
46
+ }
47
+
48
+ // Step 1: Set up local webhook receiver
49
+ console.log("āš™ļø Step 1: Setting up local webhook receiver...");
50
+
51
+ const app = express();
52
+ app.use(express.json());
53
+
54
+ // Webhook endpoint - receives events from SDK
55
+ app.post("/webhook", (req: Request, res: Response) => {
56
+ const payload = req.body;
57
+ webhookCounter++;
58
+
59
+ // Store webhook for later display
60
+ receivedWebhooks.push({
61
+ ...payload,
62
+ receivedAt: new Date().toISOString()
63
+ });
64
+
65
+ console.log(`\nšŸŽÆ Webhook #${webhookCounter} received!`);
66
+ console.log("=".repeat(80));
67
+ console.log(`Event: ${payload.event}`);
68
+ console.log(`Timestamp: ${payload.timestamp}`);
69
+
70
+ // Show specific data based on event type
71
+ if (payload.event === "task_response") {
72
+ console.log(`Agent: ${payload.data?.agentName || "Unknown"}`);
73
+ console.log(`Task ID: ${payload.data?.taskId}`);
74
+ console.log(`Success: ${payload.data?.success !== false}`);
75
+ if (payload.data?.humanized) {
76
+ const preview = payload.data.humanized.substring(0, 100);
77
+ console.log(`Response: ${preview}${payload.data.humanized.length > 100 ? "..." : ""}`);
78
+ }
79
+ } else if (payload.event === "agent_selected") {
80
+ console.log(`Agent: ${payload.data?.agent_name || "Unknown"}`);
81
+ console.log(`Reasoning: ${payload.data?.reasoning || "N/A"}`);
82
+ } else {
83
+ console.log(`Data: ${JSON.stringify(payload.data, null, 2).substring(0, 200)}`);
84
+ }
85
+
86
+ if (payload.metadata) {
87
+ console.log(
88
+ `Metadata: Room=${payload.metadata.roomId}, Agent=${payload.metadata.agentId || "N/A"}`
89
+ );
90
+ }
91
+ console.log("=".repeat(80) + "\n");
92
+
93
+ // Respond with success
94
+ res.status(200).json({
95
+ success: true,
96
+ received_at: new Date().toISOString(),
97
+ webhook_count: webhookCounter
98
+ });
99
+ });
100
+
101
+ // Health check endpoint
102
+ app.get("/health", (_req: Request, res: Response) => {
103
+ res.json({
104
+ status: "ok",
105
+ webhooksReceived: webhookCounter,
106
+ uptime: process.uptime()
107
+ });
108
+ });
109
+
110
+ // Webhook list endpoint - view all received webhooks
111
+ app.get("/webhooks", (_req: Request, res: Response) => {
112
+ res.json({
113
+ total: webhookCounter,
114
+ webhooks: receivedWebhooks
115
+ });
116
+ });
117
+
118
+ const server = app.listen(WEBHOOK_SINK_PORT, () => {
119
+ console.log(`āœ… Webhook server running on http://localhost:${WEBHOOK_SINK_PORT}`);
120
+ console.log(` POST http://localhost:${WEBHOOK_PORT}/webhook - Receive webhooks`);
121
+ console.log(` GET http://localhost:${WEBHOOK_PORT}/health - Health check`);
122
+ console.log(` GET http://localhost:${WEBHOOK_PORT}/webhooks - List all webhooks`);
123
+ console.log("");
124
+ });
125
+
126
+ // Step 2: Build SDK with webhook configuration
127
+ console.log("āš™ļø Step 2: Building SDK and configuring webhook...");
128
+
129
+ const webhookUrl = `http://localhost:${WEBHOOK_PORT}/webhook`;
130
+
131
+ const config = new SDKConfigBuilder()
132
+ .withWebSocketUrl(WS_URL)
133
+ .withAuthentication(PRIVATE_KEY)
134
+ // .withAutoJoinRooms([DEFAULT_ROOM])
135
+ .withResponseFormat({ format: "both", includeMetadata: true })
136
+ .withLogging("info")
137
+ .build();
138
+
139
+ // IMPORTANT: Allow insecure webhooks for local development
140
+ // In production, remove this and use HTTPS webhooks only
141
+ config.allowInsecureWebhooks = true;
142
+
143
+ const sdk = new TeneoSDK(config);
144
+
145
+ // Configure webhook after SDK creation (production-dashboard pattern)
146
+ // This allows runtime webhook configuration with custom headers
147
+ sdk.configureWebhook(webhookUrl, {
148
+ "X-API-Key": "webhook-example-secret",
149
+ "Content-Type": "application/json"
150
+ });
151
+
152
+ console.log(`āœ… SDK configured with webhook: ${webhookUrl}`);
153
+ console.log(" Custom headers: X-API-Key, Content-Type\n");
154
+
155
+ // Step 3: Monitor webhook events
156
+ console.log("āš™ļø Step 3: Setting up webhook event listeners...");
157
+
158
+ sdk.on("webhook:sent", (payload, url) => {
159
+ console.log(`šŸ“¤ Webhook sent to ${url}`);
160
+ console.log(` Event: ${payload.event}`);
161
+ });
162
+
163
+ sdk.on("webhook:success", (response, url) => {
164
+ console.log(`āœ… Webhook delivered successfully to ${url}`);
165
+ });
166
+
167
+ sdk.on("webhook:error", (error, url) => {
168
+ console.error(`āŒ Webhook delivery failed to ${url}:`);
169
+ console.error(` Error: ${error.message}`);
170
+ });
171
+
172
+ sdk.on("webhook:retry", (attempt, url) => {
173
+ console.log(`šŸ”„ Retrying webhook delivery (attempt ${attempt}) to ${url}`);
174
+ });
175
+
176
+ console.log("āœ… Webhook event listeners configured\n");
177
+
178
+ try {
179
+ // Step 4: Connect to Teneo
180
+ console.log("āš™ļø Step 4: Connecting to Teneo network...");
181
+ await sdk.connect();
182
+ console.log("āœ… Connected!\n");
183
+
184
+ console.log("šŸ’” Tip: While this runs, you can visit:");
185
+ console.log(` http://localhost:${WEBHOOK_PORT}/health - Check server health`);
186
+ console.log(` http://localhost:${WEBHOOK_PORT}/webhooks - View all webhooks\n`);
187
+
188
+ // Wait for agents
189
+ await new Promise((resolve) => setTimeout(resolve, 1000));
190
+
191
+ // Step 5: Trigger events that will send webhooks
192
+ console.log("āš™ļø Step 5: Triggering events to test webhook delivery...");
193
+ console.log(" The following actions will trigger webhooks:\n");
194
+
195
+ // Get agents (triggers agent:list event)
196
+ console.log(" 1. Getting agent list...");
197
+ const agents = sdk.getAgents();
198
+ console.log(` Found ${agents.length} agents`);
199
+ await new Promise((resolve) => setTimeout(resolve, 500));
200
+
201
+ // Send a message (triggers message:sent)
202
+ if (agents.length > 0) {
203
+ const agent = agents.find((a) => a.status === "online") || agents[0];
204
+
205
+ console.log(`\n 2. Sending message to ${agent.name || agent.id}...`);
206
+ await sdk.sendDirectCommand(
207
+ {
208
+ agent: agent.id,
209
+ command: "hello webhook test",
210
+ room: DEFAULT_ROOM
211
+ },
212
+ false
213
+ ); // Don't wait for response
214
+ console.log(" Message sent");
215
+ await new Promise((resolve) => setTimeout(resolve, 500));
216
+ }
217
+
218
+ // Step 6: Check webhook status
219
+ console.log("\nāš™ļø Step 6: Checking webhook status...");
220
+ const webhookStatus = sdk.getWebhookStatus();
221
+
222
+ console.log("\nšŸ“Š Webhook Status:");
223
+ console.log("=".repeat(80));
224
+ console.log(`Configured: ${webhookStatus.configured}`);
225
+ console.log(`URL: ${webhookStatus.config?.url}`);
226
+ console.log(`Pending deliveries: ${webhookStatus.queue.pending}`);
227
+ console.log(`Failed deliveries: ${webhookStatus.queue.failed}`);
228
+ console.log(`Circuit breaker state: ${webhookStatus.queue.circuitState}`);
229
+ console.log("=".repeat(80));
230
+
231
+ // Step 7: Demonstrate retry on failure
232
+ console.log("\nāš™ļø Step 7: Testing webhook retry behavior...");
233
+ console.log(" Temporarily stopping webhook server to simulate failure...\n");
234
+
235
+ // Close server to simulate failure
236
+ server.close();
237
+ console.log(" āš ļø Webhook server stopped");
238
+
239
+ // Try to send a message (webhook will fail and retry)
240
+ console.log(" Triggering event (webhook should fail and retry)...");
241
+ const testAgents = sdk.getAgents();
242
+ if (testAgents.length > 0) {
243
+ await sdk.sendDirectCommand(
244
+ {
245
+ agent: testAgents[0].id,
246
+ command: "test retry",
247
+ room: DEFAULT_ROOM
248
+ },
249
+ false
250
+ );
251
+ }
252
+
253
+ // Wait to see retry attempts
254
+ await new Promise((resolve) => setTimeout(resolve, 3000));
255
+
256
+ // Check status again
257
+ const statusAfterFailure = sdk.getWebhookStatus();
258
+ console.log("\nšŸ“Š Status after failure:");
259
+ console.log(` Pending: ${statusAfterFailure.queue.pending}`);
260
+ console.log(` Failed: ${statusAfterFailure.queue.failed}`);
261
+ console.log(` Circuit state: ${statusAfterFailure.queue.circuitState}`);
262
+
263
+ // Clear failed webhooks
264
+ if (statusAfterFailure.queue.failed > 0) {
265
+ console.log("\nāš™ļø Clearing failed webhooks...");
266
+ sdk.clearWebhookQueue();
267
+ console.log(" āœ… Queue cleared");
268
+ }
269
+
270
+ console.log("\nšŸ’” Key Points:");
271
+ console.log(" • Webhooks are sent asynchronously (non-blocking)");
272
+ console.log(" • Automatic retry with exponential backoff");
273
+ console.log(" • Circuit breaker prevents cascading failures");
274
+ console.log(" • Failed webhooks can be retried or cleared");
275
+ console.log(" • All events are logged and can be monitored");
276
+
277
+ // Display webhook summary
278
+ console.log("\nšŸ“Š Webhook Summary:");
279
+ console.log("=".repeat(80));
280
+ console.log(`Total webhooks received: ${webhookCounter}`);
281
+
282
+ if (receivedWebhooks.length > 0) {
283
+ console.log("\nWebhook events breakdown:");
284
+ const eventTypes = receivedWebhooks.reduce(
285
+ (acc, wh) => {
286
+ acc[wh.event] = (acc[wh.event] || 0) + 1;
287
+ return acc;
288
+ },
289
+ {} as Record<string, number>
290
+ );
291
+
292
+ Object.entries(eventTypes).forEach(([event, count]) => {
293
+ console.log(` ${event}: ${count}`);
294
+ });
295
+
296
+ console.log("\nRecent webhooks (last 5):");
297
+ receivedWebhooks
298
+ .slice(-5)
299
+ .reverse()
300
+ .forEach((wh, idx) => {
301
+ console.log(
302
+ ` ${idx + 1}. ${wh.event} at ${new Date(wh.receivedAt).toLocaleTimeString()}`
303
+ );
304
+ });
305
+ }
306
+ console.log("=".repeat(80));
307
+ } catch (error) {
308
+ console.error("\nāŒ Error:", error);
309
+ process.exit(1);
310
+ } finally {
311
+ sdk.disconnect();
312
+ sdk.destroy();
313
+ server.close();
314
+ console.log("\nāœ… Disconnected and cleaned up");
315
+ console.log("šŸŽ‰ Example completed!");
316
+ }
317
+ }
318
+
319
+ main().catch(console.error);
@@ -0,0 +1,396 @@
1
+ /**
2
+ * Example 6: Simple API Server
3
+ *
4
+ * This example demonstrates:
5
+ * - Building a REST API server using Express that wraps the SDK
6
+ * - Exposing SDK functionality via HTTP endpoints
7
+ * - Managing SDK lifecycle in a server context
8
+ * - Error handling and health checks
9
+ * - Production-ready server patterns
10
+ *
11
+ * Run: npx tsx examples/usage/06-simple-api-server.ts
12
+ * Then use curl or Postman to test:
13
+ * curl http://localhost:3000/health
14
+ * curl http://localhost:3000/agents
15
+ * curl -X POST http://localhost:3000/message -H "Content-Type: application/json" -d '{"message":"hello","agent":"agent-id"}'
16
+ */
17
+
18
+ import "dotenv/config";
19
+ import express from "express";
20
+ import type { Request, Response } from "express";
21
+ import { TeneoSDK, SDKConfigBuilder } from "../../dist/index.js";
22
+
23
+ // Load configuration from environment
24
+ const WS_URL =
25
+ process.env.WS_URL || "wss://your-teneo-server.com/ws";
26
+ const PRIVATE_KEY = process.env.PRIVATE_KEY || "";
27
+ const DEFAULT_ROOM = process.env.DEFAULT_ROOM || "general";
28
+ const PORT = parseInt(process.env.PORT || "3000");
29
+
30
+ // Validate configuration
31
+ if (!PRIVATE_KEY) {
32
+ console.error("āŒ ERROR: PRIVATE_KEY environment variable is required\n");
33
+ process.exit(1);
34
+ }
35
+
36
+ // Initialize Express app
37
+ const app = express();
38
+ app.use(express.json());
39
+
40
+ // Build SDK
41
+ console.log("šŸš€ Initializing Teneo API Server\n");
42
+ console.log("šŸ“‹ Configuration:");
43
+ console.log(` WebSocket: ${WS_URL}`);
44
+ console.log(` Room: ${DEFAULT_ROOM}`);
45
+ console.log(` Port: ${PORT}\n`);
46
+
47
+ const config = new SDKConfigBuilder()
48
+ .withWebSocketUrl(WS_URL)
49
+ .withAuthentication(PRIVATE_KEY)
50
+ // .withAutoJoinRooms([DEFAULT_ROOM])
51
+ .withResponseFormat({ format: "both", includeMetadata: true })
52
+ .withReconnection({ enabled: true, delay: 5000, maxAttempts: 10 })
53
+ .withLogging("info")
54
+ .build();
55
+
56
+ const sdk = new TeneoSDK(config);
57
+
58
+ // Track SDK state
59
+ let isReady = false;
60
+
61
+ // SDK event listeners
62
+ sdk.on("auth:success", (state) => {
63
+ console.log(`āœ… Authenticated as ${state.walletAddress}`);
64
+ isReady = true;
65
+ });
66
+
67
+ sdk.on("connection:close", () => {
68
+ console.log("āš ļø Connection closed");
69
+ isReady = false;
70
+ });
71
+
72
+ sdk.on("connection:reconnecting", (attempt) => {
73
+ console.log(`šŸ”„ Reconnecting... (attempt ${attempt})`);
74
+ });
75
+
76
+ sdk.on("error", (error) => {
77
+ console.error("āŒ SDK Error:", error.message);
78
+ });
79
+
80
+ // Connect to Teneo
81
+ console.log("šŸ”Œ Connecting to Teneo network...");
82
+ await sdk.connect();
83
+ console.log("āœ… Connected!\n");
84
+
85
+ // Wait for agents to load
86
+ await new Promise((resolve) => setTimeout(resolve, 1000));
87
+
88
+ // ============================================================================
89
+ // API ENDPOINTS
90
+ // ============================================================================
91
+
92
+ /**
93
+ * GET /health
94
+ * Health check endpoint - returns SDK and server status
95
+ */
96
+ app.get("/health", (_req: Request, res: Response) => {
97
+ const health = sdk.getHealth();
98
+
99
+ res.json({
100
+ status: health.status,
101
+ server: {
102
+ ready: isReady,
103
+ uptime: process.uptime()
104
+ },
105
+ teneo: {
106
+ connected: health.connection.status === "connected",
107
+ authenticated: health.connection.authenticated,
108
+ reconnectAttempts: health.connection.reconnectAttempts,
109
+ agents: health.agents.count,
110
+ rooms: health.rooms.count,
111
+ subscribedRooms: health.rooms.subscribedRooms
112
+ }
113
+ });
114
+ });
115
+
116
+ /**
117
+ * GET /agents
118
+ * List all available agents
119
+ */
120
+ app.get("/agents", (_req: Request, res: Response) => {
121
+ try {
122
+ const agents = sdk.getAgents();
123
+
124
+ res.json({
125
+ success: true,
126
+ count: agents.length,
127
+ agents: agents.map((agent) => ({
128
+ id: agent.id,
129
+ name: agent.name,
130
+ description: agent.description,
131
+ status: agent.status,
132
+ capabilities: agent.capabilities,
133
+ commands: agent.commands
134
+ }))
135
+ });
136
+ } catch (error) {
137
+ res.status(500).json({
138
+ success: false,
139
+ error: error instanceof Error ? error.message : "Unknown error"
140
+ });
141
+ }
142
+ });
143
+
144
+ /**
145
+ * GET /agents/:id
146
+ * Get specific agent by ID
147
+ */
148
+ app.get("/agents/:id", (req: Request, res: Response) => {
149
+ try {
150
+ const { id } = req.params;
151
+ const agent = sdk.getAgent(id);
152
+
153
+ if (!agent) {
154
+ return res.status(404).json({
155
+ success: false,
156
+ error: `Agent ${id} not found`
157
+ });
158
+ }
159
+
160
+ res.json({
161
+ success: true,
162
+ agent: {
163
+ id: agent.id,
164
+ name: agent.name,
165
+ description: agent.description,
166
+ status: agent.status,
167
+ capabilities: agent.capabilities,
168
+ commands: agent.commands
169
+ }
170
+ });
171
+ } catch (error) {
172
+ res.status(500).json({
173
+ success: false,
174
+ error: error instanceof Error ? error.message : "Unknown error"
175
+ });
176
+ }
177
+ });
178
+
179
+ /**
180
+ * GET /agents/capability/:capability
181
+ * Find agents by capability
182
+ */
183
+ app.get("/agents/capability/:capability", (req: Request, res: Response) => {
184
+ try {
185
+ const { capability } = req.params;
186
+ const agents = sdk.findAgentsByCapability(capability);
187
+
188
+ res.json({
189
+ success: true,
190
+ capability,
191
+ count: agents.length,
192
+ agents: agents.map((agent) => ({
193
+ id: agent.id,
194
+ name: agent.name,
195
+ status: agent.status
196
+ }))
197
+ });
198
+ } catch (error) {
199
+ res.status(500).json({
200
+ success: false,
201
+ error: error instanceof Error ? error.message : "Unknown error"
202
+ });
203
+ }
204
+ });
205
+
206
+ /**
207
+ * POST /message
208
+ * Send a message to an agent
209
+ * Body: { message: string, agent?: string, waitForResponse?: boolean, timeout?: number }
210
+ */
211
+ app.post("/message", async (req: Request, res: Response) => {
212
+ try {
213
+ const { message, agent, waitForResponse = false, timeout = 30000 } = req.body;
214
+
215
+ if (!message) {
216
+ return res.status(400).json({
217
+ success: false,
218
+ error: "Message is required"
219
+ });
220
+ }
221
+
222
+ if (!isReady) {
223
+ return res.status(503).json({
224
+ success: false,
225
+ error: "SDK is not ready yet"
226
+ });
227
+ }
228
+
229
+ const startTime = Date.now();
230
+
231
+ if (agent) {
232
+ // Send to specific agent
233
+ const response = await sdk.sendDirectCommand(
234
+ {
235
+ agent,
236
+ command: message,
237
+ room: DEFAULT_ROOM
238
+ },
239
+ waitForResponse
240
+ );
241
+
242
+ if (waitForResponse && response) {
243
+ res.json({
244
+ success: true,
245
+ agent,
246
+ response: {
247
+ humanized: response.humanized,
248
+ raw: response.raw,
249
+ metadata: response.metadata
250
+ },
251
+ duration: Date.now() - startTime
252
+ });
253
+ } else {
254
+ res.json({
255
+ success: true,
256
+ agent,
257
+ message: "Message sent (no response requested)",
258
+ duration: Date.now() - startTime
259
+ });
260
+ }
261
+ } else {
262
+ // Send via coordinator (will auto-select agent)
263
+ const response = await sdk.sendMessage(message, {
264
+ room: DEFAULT_ROOM,
265
+ waitForResponse,
266
+ timeout
267
+ });
268
+
269
+ if (waitForResponse && response) {
270
+ res.json({
271
+ success: true,
272
+ response: {
273
+ humanized: response.humanized,
274
+ raw: response.raw,
275
+ metadata: response.metadata
276
+ },
277
+ duration: Date.now() - startTime
278
+ });
279
+ } else {
280
+ res.json({
281
+ success: true,
282
+ message: "Message sent to coordinator (no response requested)",
283
+ duration: Date.now() - startTime
284
+ });
285
+ }
286
+ }
287
+ } catch (error) {
288
+ res.status(500).json({
289
+ success: false,
290
+ error: error instanceof Error ? error.message : "Unknown error"
291
+ });
292
+ }
293
+ });
294
+
295
+ /**
296
+ * GET /rooms
297
+ * List subscribed rooms
298
+ */
299
+ app.get("/rooms", (_req: Request, res: Response) => {
300
+ try {
301
+ const rooms = sdk.getSubscribedRooms();
302
+
303
+ res.json({
304
+ success: true,
305
+ rooms
306
+ });
307
+ } catch (error) {
308
+ res.status(500).json({
309
+ success: false,
310
+ error: error instanceof Error ? error.message : "Unknown error"
311
+ });
312
+ }
313
+ });
314
+
315
+ /**
316
+ * POST /rooms/:roomId/subscribe
317
+ * Subscribe to a room
318
+ */
319
+ app.post("/rooms/:roomId/subscribe", async (req: Request, res: Response) => {
320
+ try {
321
+ const { roomId } = req.params;
322
+ await sdk.subscribeToRoom(roomId);
323
+
324
+ res.json({
325
+ success: true,
326
+ message: `Subscribed to room ${roomId}`,
327
+ subscribedRooms: sdk.getSubscribedRooms()
328
+ });
329
+ } catch (error) {
330
+ res.status(500).json({
331
+ success: false,
332
+ error: error instanceof Error ? error.message : "Unknown error"
333
+ });
334
+ }
335
+ });
336
+
337
+ /**
338
+ * POST /rooms/:roomId/unsubscribe
339
+ * Unsubscribe from a room
340
+ */
341
+ app.post("/rooms/:roomId/unsubscribe", async (req: Request, res: Response) => {
342
+ try {
343
+ const { roomId } = req.params;
344
+ await sdk.unsubscribeFromRoom(roomId);
345
+
346
+ res.json({
347
+ success: true,
348
+ message: `Unsubscribed from room ${roomId}`,
349
+ subscribedRooms: sdk.getSubscribedRooms()
350
+ });
351
+ } catch (error) {
352
+ res.status(500).json({
353
+ success: false,
354
+ error: error instanceof Error ? error.message : "Unknown error"
355
+ });
356
+ }
357
+ });
358
+
359
+ // ============================================================================
360
+ // START SERVER
361
+ // ============================================================================
362
+
363
+ const server = app.listen(PORT, () => {
364
+ console.log("šŸš€ Teneo API Server running!\n");
365
+ console.log("šŸ“” Endpoints:");
366
+ console.log(` GET http://localhost:${PORT}/health`);
367
+ console.log(` GET http://localhost:${PORT}/agents`);
368
+ console.log(` GET http://localhost:${PORT}/agents/:id`);
369
+ console.log(` GET http://localhost:${PORT}/agents/capability/:capability`);
370
+ console.log(` POST http://localhost:${PORT}/message`);
371
+ console.log(` GET http://localhost:${PORT}/rooms`);
372
+ console.log(` POST http://localhost:${PORT}/rooms/:roomId/subscribe`);
373
+ console.log(` POST http://localhost:${PORT}/rooms/:roomId/unsubscribe`);
374
+ console.log("\nšŸ“ Example requests:");
375
+ console.log(` curl http://localhost:${PORT}/health`);
376
+ console.log(` curl http://localhost:${PORT}/agents`);
377
+ console.log(
378
+ ` curl -X POST http://localhost:${PORT}/message -H "Content-Type: application/json" -d '{"message":"hello","waitForResponse":true}'`
379
+ );
380
+ console.log("\nāœ… Server ready to accept requests!\n");
381
+ });
382
+
383
+ // Graceful shutdown
384
+ process.on("SIGINT", () => {
385
+ console.log("\n\nšŸ‘‹ Shutting down gracefully...");
386
+
387
+ server.close(() => {
388
+ console.log("āœ… HTTP server closed");
389
+ });
390
+
391
+ sdk.disconnect();
392
+ sdk.destroy();
393
+ console.log("āœ… SDK disconnected");
394
+
395
+ process.exit(0);
396
+ });