@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.
- package/.github/workflows/publish-npm.yml +8 -6
- package/CHANGELOG.md +265 -0
- package/README.md +406 -53
- package/dist/core/websocket-client.d.ts +13 -0
- package/dist/core/websocket-client.d.ts.map +1 -1
- package/dist/core/websocket-client.js +34 -3
- package/dist/core/websocket-client.js.map +1 -1
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.js +70 -0
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts +92 -38
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/agent-status-update-handler.d.ts +904 -0
- package/dist/handlers/message-handlers/agent-status-update-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-status-update-handler.js +51 -0
- package/dist/handlers/message-handlers/agent-status-update-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts +45 -31
- package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-message-handler.d.ts +6 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-message-handler.js +65 -5
- package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -1
- package/dist/handlers/message-handlers/auth-required-handler.d.ts +49 -31
- package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-success-handler.d.ts +6 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-success-handler.js +46 -4
- package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -1
- package/dist/handlers/message-handlers/challenge-handler.d.ts +45 -31
- package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/error-message-handler.d.ts +49 -31
- package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/index.d.ts +5 -0
- package/dist/handlers/message-handlers/index.d.ts.map +1 -1
- package/dist/handlers/message-handlers/index.js +23 -1
- package/dist/handlers/message-handlers/index.js.map +1 -1
- package/dist/handlers/message-handlers/list-available-agents-handler.d.ts +877 -0
- package/dist/handlers/message-handlers/list-available-agents-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-available-agents-handler.js +38 -0
- package/dist/handlers/message-handlers/list-available-agents-handler.js.map +1 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.d.ts +886 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.js +51 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.js.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +178 -89
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts +62 -58
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/regular-message-handler.d.ts +31 -29
- package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/regular-message-handler.js +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -1
- package/dist/handlers/message-handlers/room-operation-response-handler.d.ts +328 -0
- package/dist/handlers/message-handlers/room-operation-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/room-operation-response-handler.js +92 -0
- package/dist/handlers/message-handlers/room-operation-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +53 -31
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/types.d.ts +2 -0
- package/dist/handlers/message-handlers/types.d.ts.map +1 -1
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +53 -31
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -1
- package/dist/managers/agent-room-manager.d.ts +222 -0
- package/dist/managers/agent-room-manager.d.ts.map +1 -0
- package/dist/managers/agent-room-manager.js +508 -0
- package/dist/managers/agent-room-manager.js.map +1 -0
- package/dist/managers/index.d.ts +2 -0
- package/dist/managers/index.d.ts.map +1 -1
- package/dist/managers/index.js +5 -1
- package/dist/managers/index.js.map +1 -1
- package/dist/managers/message-router.d.ts +1 -1
- package/dist/managers/message-router.d.ts.map +1 -1
- package/dist/managers/message-router.js +41 -4
- package/dist/managers/message-router.js.map +1 -1
- package/dist/managers/room-management-manager.d.ts +213 -0
- package/dist/managers/room-management-manager.d.ts.map +1 -0
- package/dist/managers/room-management-manager.js +440 -0
- package/dist/managers/room-management-manager.js.map +1 -0
- package/dist/managers/room-manager.d.ts +4 -4
- package/dist/managers/room-manager.d.ts.map +1 -1
- package/dist/managers/room-manager.js +1 -1
- package/dist/managers/room-manager.js.map +1 -1
- package/dist/teneo-sdk.d.ts +362 -14
- package/dist/teneo-sdk.d.ts.map +1 -1
- package/dist/teneo-sdk.js +497 -7
- package/dist/teneo-sdk.js.map +1 -1
- package/dist/types/config.d.ts +63 -54
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +9 -5
- package/dist/types/config.js.map +1 -1
- package/dist/types/error-codes.d.ts +2 -0
- package/dist/types/error-codes.d.ts.map +1 -1
- package/dist/types/error-codes.js +3 -0
- package/dist/types/error-codes.js.map +1 -1
- package/dist/types/events.d.ts +132 -68
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/events.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +27 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/messages.d.ts +11396 -2559
- package/dist/types/messages.d.ts.map +1 -1
- package/dist/types/messages.js +294 -27
- package/dist/types/messages.js.map +1 -1
- package/dist/types/validation.d.ts.map +1 -1
- package/dist/types/validation.js +1 -1
- package/dist/types/validation.js.map +1 -1
- package/dist/utils/bounded-queue.d.ts +1 -1
- package/dist/utils/bounded-queue.js +6 -6
- package/dist/utils/circuit-breaker.d.ts.map +1 -1
- package/dist/utils/circuit-breaker.js.map +1 -1
- package/dist/utils/event-waiter.d.ts.map +1 -1
- package/dist/utils/event-waiter.js +2 -1
- package/dist/utils/event-waiter.js.map +1 -1
- package/dist/utils/rate-limiter.d.ts.map +1 -1
- package/dist/utils/rate-limiter.js +4 -6
- package/dist/utils/rate-limiter.js.map +1 -1
- package/dist/utils/secure-private-key.d.ts.map +1 -1
- package/dist/utils/secure-private-key.js +9 -15
- package/dist/utils/secure-private-key.js.map +1 -1
- package/dist/utils/signature-verifier.d.ts +2 -2
- package/dist/utils/signature-verifier.d.ts.map +1 -1
- package/dist/utils/signature-verifier.js +5 -5
- package/dist/utils/signature-verifier.js.map +1 -1
- package/examples/.env.example +1 -1
- package/examples/agent-room-management-example.ts +334 -0
- package/examples/claude-agent-x-follower/.env.example +117 -0
- package/examples/claude-agent-x-follower/QUICKSTART.md +243 -0
- package/examples/claude-agent-x-follower/README.md +540 -0
- package/examples/claude-agent-x-follower/index.ts +248 -0
- package/examples/claude-agent-x-follower/package.json +37 -0
- package/examples/claude-agent-x-follower/tsconfig.json +20 -0
- package/examples/n8n-teneo/.env.example +127 -0
- package/examples/n8n-teneo/Dockerfile +42 -0
- package/examples/n8n-teneo/README.md +564 -0
- package/examples/n8n-teneo/docker-compose.yml +71 -0
- package/examples/n8n-teneo/index.ts +177 -0
- package/examples/n8n-teneo/package.json +22 -0
- package/examples/n8n-teneo/tsconfig.json +12 -0
- package/examples/n8n-teneo/workflows/x-timeline.json +66 -0
- package/examples/openai-teneo/.env.example +130 -0
- package/examples/openai-teneo/README.md +635 -0
- package/examples/openai-teneo/index.ts +280 -0
- package/examples/openai-teneo/package.json +24 -0
- package/examples/openai-teneo/tsconfig.json +16 -0
- package/examples/production-dashboard/.env.example +5 -3
- package/examples/production-dashboard/README.md +839 -0
- package/examples/production-dashboard/pnpm-lock.yaml +92 -0
- package/examples/production-dashboard/public/dashboard.html +1150 -504
- package/examples/production-dashboard/server.ts +428 -12
- package/examples/room-management-example.ts +285 -0
- package/examples/usage/.env.example +17 -0
- package/examples/usage/01-connect.ts +116 -0
- package/examples/usage/02-list-agents.ts +153 -0
- package/examples/usage/03-pick-agent.ts +201 -0
- package/examples/usage/04-find-by-capability.ts +237 -0
- package/examples/usage/05-webhook-example.ts +319 -0
- package/examples/usage/06-simple-api-server.ts +396 -0
- package/examples/usage/07-event-listener.ts +402 -0
- package/examples/usage/README.md +383 -0
- package/examples/usage/package.json +42 -0
- package/package.json +13 -3
- package/src/core/websocket-client.ts +43 -9
- package/src/formatters/response-formatter.test.ts +8 -2
- package/src/handlers/message-handlers/agent-room-operation-response-handler.ts +83 -0
- package/src/handlers/message-handlers/agent-status-update-handler.ts +58 -0
- package/src/handlers/message-handlers/auth-message-handler.ts +73 -5
- package/src/handlers/message-handlers/auth-success-handler.ts +58 -6
- package/src/handlers/message-handlers/index.ts +19 -0
- package/src/handlers/message-handlers/list-available-agents-handler.ts +41 -0
- package/src/handlers/message-handlers/list-room-agents-handler.ts +61 -0
- package/src/handlers/message-handlers/regular-message-handler.ts +1 -0
- package/src/handlers/message-handlers/room-operation-response-handler.ts +105 -0
- package/src/handlers/message-handlers/types.ts +6 -0
- package/src/handlers/webhook-handler.test.ts +13 -10
- package/src/managers/agent-room-manager.ts +609 -0
- package/src/managers/index.ts +2 -0
- package/src/managers/message-router.ts +48 -6
- package/src/managers/room-management-manager.ts +523 -0
- package/src/managers/room-manager.ts +12 -6
- package/src/teneo-sdk.ts +543 -10
- package/src/types/config.ts +13 -6
- package/src/types/error-codes.ts +4 -0
- package/src/types/events.ts +24 -0
- package/src/types/index.ts +55 -0
- package/src/types/messages.ts +374 -41
- package/src/types/validation.ts +4 -1
- package/src/utils/bounded-queue.ts +9 -9
- package/src/utils/circuit-breaker.ts +4 -1
- package/src/utils/deduplication-cache.test.ts +2 -6
- package/src/utils/event-waiter.test.ts +4 -1
- package/src/utils/event-waiter.ts +5 -7
- package/src/utils/rate-limiter.test.ts +5 -17
- package/src/utils/rate-limiter.ts +6 -9
- package/src/utils/secure-private-key.test.ts +66 -59
- package/src/utils/secure-private-key.ts +10 -16
- package/src/utils/signature-verifier.test.ts +75 -70
- package/src/utils/signature-verifier.ts +7 -8
- package/src/utils/ssrf-validator.test.ts +3 -3
- package/tests/integration/room-management.test.ts +514 -0
- package/tests/integration/websocket.test.ts +1 -1
- package/tests/unit/handlers/agent-room-operation-response-handler.test.ts +394 -0
- package/tests/unit/handlers/agent-status-update-handler.test.ts +407 -0
- package/tests/unit/handlers/auth-success-handler-rooms.test.ts +699 -0
- package/tests/unit/handlers/list-available-agents-handler.test.ts +256 -0
- package/tests/unit/handlers/list-room-agents-handler.test.ts +294 -0
- package/tests/unit/handlers/room-operation-response-handler.test.ts +527 -0
- package/tests/unit/managers/agent-room-manager.test.ts +534 -0
- 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
|
+
});
|