@teneo-protocol/sdk 1.0.0 → 1.0.1
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/dist/core/websocket-client.d.ts +1 -0
- package/dist/core/websocket-client.d.ts.map +1 -1
- package/dist/core/websocket-client.js +12 -1
- package/dist/core/websocket-client.js.map +1 -1
- 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/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-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 +29 -1
- package/dist/teneo-sdk.d.ts.map +1 -1
- package/dist/teneo-sdk.js +29 -6
- package/dist/teneo-sdk.js.map +1 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +1 -1
- package/dist/types/config.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/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 +762 -0
- package/examples/production-dashboard/pnpm-lock.yaml +92 -0
- package/examples/production-dashboard/public/dashboard.html +84 -10
- package/examples/production-dashboard/server.ts +83 -9
- 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 +5 -3
- package/src/core/websocket-client.ts +17 -7
- package/src/formatters/response-formatter.test.ts +8 -2
- package/src/handlers/message-handlers/regular-message-handler.ts +1 -0
- package/src/handlers/webhook-handler.test.ts +13 -10
- package/src/managers/message-router.ts +48 -6
- package/src/managers/room-manager.ts +9 -2
- package/src/teneo-sdk.ts +39 -7
- package/src/types/config.ts +3 -1
- 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
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# Teneo SDK Usage Examples
|
|
2
|
+
|
|
3
|
+
This directory contains progressive, hands-on examples demonstrating how to use the Teneo Consumer SDK. Each example builds on concepts from the previous ones, taking you from basic connection to building production-ready applications.
|
|
4
|
+
|
|
5
|
+
## 📚 Examples Overview
|
|
6
|
+
|
|
7
|
+
| # | Example | Description | Key Concepts |
|
|
8
|
+
|---|---------|-------------|--------------|
|
|
9
|
+
| 1 | [Connect](#1-basic-connection) | Basic WebSocket connection | Config builder, authentication, lifecycle |
|
|
10
|
+
| 2 | [List Agents](#2-list-agents) | Retrieve available agents | Agent registry, properties, statistics |
|
|
11
|
+
| 3 | [Pick Agent](#3-pick-specific-agent) | Direct agent communication | sendDirectCommand, response handling |
|
|
12
|
+
| 4 | [Find by Capability](#4-find-by-capability) | Indexed agent search | O(1) lookups, capability matching |
|
|
13
|
+
| 5 | [Webhooks](#5-webhook-integration) | HTTP event notifications | Webhook setup, retry logic, circuit breaker |
|
|
14
|
+
| 6 | [API Server](#6-simple-api-server) | REST API wrapper | Express integration, endpoint design |
|
|
15
|
+
| 7 | [Event Listener](#7-event-listener) | Event-driven patterns | 30+ events, real-time monitoring |
|
|
16
|
+
|
|
17
|
+
## 🚀 Prerequisites
|
|
18
|
+
|
|
19
|
+
Before running these examples, make sure you have:
|
|
20
|
+
|
|
21
|
+
1. **Node.js 18+** installed
|
|
22
|
+
2. **Built the SDK**: Run `npm run build` in the project root
|
|
23
|
+
3. **Environment variables** set up (see below)
|
|
24
|
+
|
|
25
|
+
## ⚙️ Environment Setup
|
|
26
|
+
|
|
27
|
+
Create a `.env` file in the project root or export these variables:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Required
|
|
31
|
+
PRIVATE_KEY=your_ethereum_private_key_here
|
|
32
|
+
WS_URL=wss://dev-rooms-websocket-ai-core-o9fmb.ondigitalocean.app/ws
|
|
33
|
+
|
|
34
|
+
# Optional
|
|
35
|
+
DEFAULT_ROOM=general
|
|
36
|
+
WALLET_ADDRESS=0x... # Auto-derived if not provided
|
|
37
|
+
LOG_LEVEL=info
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 📖 Usage Examples
|
|
41
|
+
|
|
42
|
+
### 1. Basic Connection
|
|
43
|
+
|
|
44
|
+
**File**: `01-connect.ts`
|
|
45
|
+
|
|
46
|
+
The simplest example - connect to Teneo and authenticate.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx tsx examples/usage/01-connect.ts
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**What you'll learn**:
|
|
53
|
+
- Using `SDKConfigBuilder` for configuration
|
|
54
|
+
- Connecting to the WebSocket server
|
|
55
|
+
- Ethereum wallet authentication
|
|
56
|
+
- Event listeners for connection lifecycle
|
|
57
|
+
- Graceful disconnection and cleanup
|
|
58
|
+
|
|
59
|
+
**Output**:
|
|
60
|
+
```
|
|
61
|
+
🚀 Example 1: Basic SDK Connection
|
|
62
|
+
⚙️ Step 1: Building SDK configuration...
|
|
63
|
+
✅ Configuration built
|
|
64
|
+
...
|
|
65
|
+
✅ Connected and authenticated!
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
### 2. List Agents
|
|
71
|
+
|
|
72
|
+
**File**: `02-list-agents.ts`
|
|
73
|
+
|
|
74
|
+
Retrieve and inspect all available agents in the network.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx tsx examples/usage/02-list-agents.ts
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**What you'll learn**:
|
|
81
|
+
- Getting the agent list
|
|
82
|
+
- Inspecting agent properties (name, description, status)
|
|
83
|
+
- Viewing agent capabilities and commands
|
|
84
|
+
- Getting statistics (online/offline counts)
|
|
85
|
+
|
|
86
|
+
**Output**:
|
|
87
|
+
```
|
|
88
|
+
📊 Agent Details:
|
|
89
|
+
1. X Platform Agent
|
|
90
|
+
ID: x-agent-001
|
|
91
|
+
Status: online
|
|
92
|
+
Capabilities: social-media, twitter
|
|
93
|
+
...
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### 3. Pick Specific Agent
|
|
99
|
+
|
|
100
|
+
**File**: `03-pick-agent.ts`
|
|
101
|
+
|
|
102
|
+
Find and communicate with a specific agent.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Pick by name
|
|
106
|
+
npx tsx examples/usage/03-pick-agent.ts "X Platform Agent"
|
|
107
|
+
|
|
108
|
+
# Or let it auto-select
|
|
109
|
+
npx tsx examples/usage/03-pick-agent.ts
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**What you'll learn**:
|
|
113
|
+
- Finding agents by ID or name
|
|
114
|
+
- Sending direct commands to agents
|
|
115
|
+
- Waiting for responses with timeout
|
|
116
|
+
- Handling both humanized and raw responses
|
|
117
|
+
- Response metadata (task ID, duration, etc.)
|
|
118
|
+
|
|
119
|
+
**Output**:
|
|
120
|
+
```
|
|
121
|
+
✅ Selected: X Platform Agent
|
|
122
|
+
⚙️ Sending command to agent...
|
|
123
|
+
Command: timeline @elonmusk 3
|
|
124
|
+
Waiting for response...
|
|
125
|
+
✅ Response received!
|
|
126
|
+
📝 Humanized Response: [response content]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### 4. Find by Capability
|
|
132
|
+
|
|
133
|
+
**File**: `04-find-by-capability.ts`
|
|
134
|
+
|
|
135
|
+
Use the SDK's indexed search for efficient agent discovery.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npx tsx examples/usage/04-find-by-capability.ts
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**What you'll learn**:
|
|
142
|
+
- **PERF-3**: O(1) capability lookups
|
|
143
|
+
- O(1) status lookups (online/offline)
|
|
144
|
+
- O(k) name-based token search
|
|
145
|
+
- Performance comparison
|
|
146
|
+
- Practical agent selection strategies
|
|
147
|
+
|
|
148
|
+
**Output**:
|
|
149
|
+
```
|
|
150
|
+
🔍 Searching for capability: "weather-forecast"
|
|
151
|
+
✅ Found 2 agent(s) (0.123ms):
|
|
152
|
+
• Weather Agent
|
|
153
|
+
• Climate Data Agent
|
|
154
|
+
|
|
155
|
+
📊 Performance Summary:
|
|
156
|
+
• Capability search: O(1) - constant time
|
|
157
|
+
• Status search: O(1) - constant time
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### 5. Webhook Integration
|
|
163
|
+
|
|
164
|
+
**File**: `05-webhook-example.ts`
|
|
165
|
+
|
|
166
|
+
Set up HTTP webhooks to receive SDK events.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npx tsx examples/usage/05-webhook-example.ts
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**What you'll learn**:
|
|
173
|
+
- Creating a local webhook receiver
|
|
174
|
+
- Configuring SDK webhook delivery
|
|
175
|
+
- Receiving webhook events
|
|
176
|
+
- Automatic retry with exponential backoff
|
|
177
|
+
- Circuit breaker pattern
|
|
178
|
+
- Queue management
|
|
179
|
+
|
|
180
|
+
**Output**:
|
|
181
|
+
```
|
|
182
|
+
✅ Webhook server running on http://localhost:3001
|
|
183
|
+
🎯 Webhook received!
|
|
184
|
+
Event: agent:response
|
|
185
|
+
Timestamp: 2025-10-28T...
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### 6. Simple API Server
|
|
191
|
+
|
|
192
|
+
**File**: `06-simple-api-server.ts`
|
|
193
|
+
|
|
194
|
+
Build a REST API that wraps the Teneo SDK.
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npx tsx examples/usage/06-simple-api-server.ts
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Then test with curl:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Health check
|
|
204
|
+
curl http://localhost:3000/health
|
|
205
|
+
|
|
206
|
+
# List agents
|
|
207
|
+
curl http://localhost:3000/agents
|
|
208
|
+
|
|
209
|
+
# Send message
|
|
210
|
+
curl -X POST http://localhost:3000/message \
|
|
211
|
+
-H "Content-Type: application/json" \
|
|
212
|
+
-d '{"message":"hello","waitForResponse":true}'
|
|
213
|
+
|
|
214
|
+
# Find by capability
|
|
215
|
+
curl http://localhost:3000/agents/capability/weather-forecast
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**What you'll learn**:
|
|
219
|
+
- Building Express REST API
|
|
220
|
+
- Wrapping SDK in HTTP endpoints
|
|
221
|
+
- Error handling patterns
|
|
222
|
+
- Health monitoring
|
|
223
|
+
- Production-ready server design
|
|
224
|
+
|
|
225
|
+
**Endpoints**:
|
|
226
|
+
- `GET /health` - Server and SDK health
|
|
227
|
+
- `GET /agents` - List all agents
|
|
228
|
+
- `GET /agents/:id` - Get specific agent
|
|
229
|
+
- `GET /agents/capability/:capability` - Find by capability
|
|
230
|
+
- `POST /message` - Send message to agent
|
|
231
|
+
- `GET /rooms` - List rooms
|
|
232
|
+
- `POST /rooms/:roomId/subscribe` - Subscribe to room
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
### 7. Event Listener
|
|
237
|
+
|
|
238
|
+
**File**: `07-event-listener.ts`
|
|
239
|
+
|
|
240
|
+
Listen to all SDK events for real-time monitoring.
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
npx tsx examples/usage/07-event-listener.ts
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**What you'll learn**:
|
|
247
|
+
- All 30+ SDK event types
|
|
248
|
+
- Event categorization (connection, auth, agent, message, etc.)
|
|
249
|
+
- Real-time event monitoring
|
|
250
|
+
- Event-driven architecture
|
|
251
|
+
- Statistics and analytics
|
|
252
|
+
|
|
253
|
+
**Output**:
|
|
254
|
+
```
|
|
255
|
+
🔌 [CONNECTION] WebSocket connection opened
|
|
256
|
+
🔐 [AUTH] ✅ Authentication successful!
|
|
257
|
+
🤖 [AGENT] Agent list updated: 5 agents
|
|
258
|
+
📤 [MESSAGE] Message sent: Type: message
|
|
259
|
+
...
|
|
260
|
+
📊 EVENT STATISTICS
|
|
261
|
+
Connection events: 12
|
|
262
|
+
Authentication events: 4
|
|
263
|
+
Agent events: 8
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## 🎯 Learning Path
|
|
269
|
+
|
|
270
|
+
We recommend following this learning path:
|
|
271
|
+
|
|
272
|
+
1. **Start with 01-connect.ts** to understand basic setup
|
|
273
|
+
2. **Try 02-list-agents.ts** to explore the agent registry
|
|
274
|
+
3. **Run 03-pick-agent.ts** to learn agent communication
|
|
275
|
+
4. **Explore 04-find-by-capability.ts** for efficient search
|
|
276
|
+
5. **Set up 05-webhook-example.ts** for event notifications
|
|
277
|
+
6. **Build 06-simple-api-server.ts** for production patterns
|
|
278
|
+
7. **Monitor with 07-event-listener.ts** for observability
|
|
279
|
+
|
|
280
|
+
## 🔧 Common Patterns
|
|
281
|
+
|
|
282
|
+
### Pattern 1: Basic Message Flow
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
const sdk = new TeneoSDK(config);
|
|
286
|
+
await sdk.connect();
|
|
287
|
+
|
|
288
|
+
// Send and wait for response
|
|
289
|
+
const response = await sdk.sendMessage('hello', {
|
|
290
|
+
room: 'general',
|
|
291
|
+
waitForResponse: true,
|
|
292
|
+
timeout: 30000
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
console.log(response.humanized);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Pattern 2: Direct Agent Command
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
// Find agent by capability
|
|
302
|
+
const agents = sdk.findAgentsByCapability('weather');
|
|
303
|
+
const weatherAgent = agents[0];
|
|
304
|
+
|
|
305
|
+
// Send direct command
|
|
306
|
+
const response = await sdk.sendDirectCommand({
|
|
307
|
+
agent: weatherAgent.id,
|
|
308
|
+
command: 'forecast for NYC',
|
|
309
|
+
room: 'general'
|
|
310
|
+
}, true);
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Pattern 3: Event-Driven
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
sdk.on('agent:response', (response) => {
|
|
317
|
+
console.log('Response:', response.humanized);
|
|
318
|
+
// Process response asynchronously
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Fire and forget
|
|
322
|
+
await sdk.sendMessage('hello', { room: 'general' });
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## 🐛 Troubleshooting
|
|
326
|
+
|
|
327
|
+
### "PRIVATE_KEY is required"
|
|
328
|
+
|
|
329
|
+
Set the environment variable:
|
|
330
|
+
```bash
|
|
331
|
+
export PRIVATE_KEY=your_private_key_here
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### "No agents available"
|
|
335
|
+
|
|
336
|
+
Wait a bit longer after connecting:
|
|
337
|
+
```typescript
|
|
338
|
+
await sdk.connect();
|
|
339
|
+
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2s
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### "Webhook delivery failed"
|
|
343
|
+
|
|
344
|
+
Ensure your webhook URL is accessible:
|
|
345
|
+
- Use `http://localhost:PORT` for local testing
|
|
346
|
+
- Use HTTPS for production webhooks
|
|
347
|
+
- Check firewall settings
|
|
348
|
+
|
|
349
|
+
### "Connection timeout"
|
|
350
|
+
|
|
351
|
+
Check your WebSocket URL and network:
|
|
352
|
+
```typescript
|
|
353
|
+
const config = new SDKConfigBuilder()
|
|
354
|
+
.withWebSocketUrl('wss://correct-url.com/ws')
|
|
355
|
+
.withReconnection({ enabled: true, maxAttempts: 10 })
|
|
356
|
+
.build();
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## 📚 Next Steps
|
|
360
|
+
|
|
361
|
+
After completing these examples, check out:
|
|
362
|
+
|
|
363
|
+
- **[Integration Examples](../INTEGRATION_EXAMPLES.md)** - Claude, OpenAI, n8n integrations
|
|
364
|
+
- **[Production Dashboard](../production-dashboard/)** - Full-featured monitoring UI
|
|
365
|
+
- **[API Documentation](../../docs/)** - Complete API reference
|
|
366
|
+
- **[Main README](../../README.md)** - Full SDK documentation
|
|
367
|
+
|
|
368
|
+
## 💡 Tips
|
|
369
|
+
|
|
370
|
+
1. **Always build the SDK first**: Run `npm run build` before running examples
|
|
371
|
+
2. **Use environment variables**: Don't hardcode credentials
|
|
372
|
+
3. **Enable debug logging**: Set `LOG_LEVEL=debug` for troubleshooting
|
|
373
|
+
4. **Check health status**: Use `sdk.getHealth()` to monitor SDK state
|
|
374
|
+
5. **Handle errors**: Always use try-catch blocks in production code
|
|
375
|
+
|
|
376
|
+
## 🤝 Contributing
|
|
377
|
+
|
|
378
|
+
Found an issue or want to add an example? Please open an issue or PR!
|
|
379
|
+
|
|
380
|
+
## 📄 License
|
|
381
|
+
|
|
382
|
+
MIT - See [LICENSE](../../LICENSE) for details
|
|
383
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@teneo/usage-examples",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Step-by-step usage examples for Teneo Protocol SDK",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"01:connect": "tsx 01-connect.ts",
|
|
9
|
+
"02:list-agents": "tsx 02-list-agents.ts",
|
|
10
|
+
"03:pick-agent": "tsx 03-pick-agent.ts",
|
|
11
|
+
"04:find-by-capability": "tsx 04-find-by-capability.ts",
|
|
12
|
+
"05:webhook": "tsx 05-webhook-example.ts",
|
|
13
|
+
"06:api-server": "tsx 06-simple-api-server.ts",
|
|
14
|
+
"07:event-listener": "tsx 07-event-listener.ts",
|
|
15
|
+
"all": "npm run 01:connect && npm run 02:list-agents && npm run 03:pick-agent && npm run 04:find-by-capability"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"dotenv": "^16.4.0",
|
|
19
|
+
"express": "^4.18.2"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/express": "^4.17.21",
|
|
23
|
+
"@types/node": "^20.10.5",
|
|
24
|
+
"tsx": "^4.7.0",
|
|
25
|
+
"typescript": "^5.3.3"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18.0.0"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"teneo",
|
|
32
|
+
"sdk",
|
|
33
|
+
"examples",
|
|
34
|
+
"usage",
|
|
35
|
+
"tutorial",
|
|
36
|
+
"websocket",
|
|
37
|
+
"ai",
|
|
38
|
+
"agents"
|
|
39
|
+
],
|
|
40
|
+
"author": "Teneo Protocol",
|
|
41
|
+
"license": "MIT"
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teneo-protocol/sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "TypeScript SDK for external platforms to interact with Teneo agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"license": "AGPL-3.0",
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"eventemitter3": "^5.0.1",
|
|
20
|
-
"hono": "^4.9.12",
|
|
21
20
|
"node-fetch": "^3.3.2",
|
|
22
21
|
"pino": "^8.17.2",
|
|
23
22
|
"uuid": "^9.0.1",
|
|
@@ -60,6 +59,9 @@
|
|
|
60
59
|
"lint:fix": "eslint . --ext .ts --fix",
|
|
61
60
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
62
61
|
"example:battle": "ts-node examples/x-influencer-battle-server.ts",
|
|
63
|
-
"example:dashboard": "cd examples/production-dashboard && bun run server.ts"
|
|
62
|
+
"example:dashboard": "cd examples/production-dashboard && bun run server.ts",
|
|
63
|
+
"example:claude": "tsx examples/claude-agent-x-follower/index.ts",
|
|
64
|
+
"example:openai": "cd examples/openai-teneo && tsx index.ts",
|
|
65
|
+
"example:n8n": "cd examples/n8n-teneo && tsx index.ts"
|
|
64
66
|
}
|
|
65
67
|
}
|
|
@@ -56,6 +56,7 @@ export class WebSocketClient extends EventEmitter<SDKEvents> {
|
|
|
56
56
|
private deduplicationCache?: DeduplicationCache;
|
|
57
57
|
private reconnectPolicy: RetryPolicy;
|
|
58
58
|
private roomManager?: any; // Reference to RoomManager for handler context
|
|
59
|
+
private intentionalDisconnect: boolean = false; // Track intentional disconnect to prevent reconnection
|
|
59
60
|
|
|
60
61
|
private connectionState: ConnectionState = {
|
|
61
62
|
connected: false,
|
|
@@ -94,15 +95,13 @@ export class WebSocketClient extends EventEmitter<SDKEvents> {
|
|
|
94
95
|
if (config.privateKey) {
|
|
95
96
|
try {
|
|
96
97
|
// Check if privateKey is already a SecurePrivateKey instance (SEC-3)
|
|
97
|
-
if (typeof config.privateKey ===
|
|
98
|
+
if (typeof config.privateKey === "object" && "use" in config.privateKey) {
|
|
98
99
|
// Use the provided SecurePrivateKey directly
|
|
99
100
|
this.secureKey = config.privateKey;
|
|
100
101
|
this.ownsSecureKey = false; // User provided it, we don't own it
|
|
101
102
|
|
|
102
103
|
// Create account using the secure key
|
|
103
|
-
this.account = this.secureKey.use((key) =>
|
|
104
|
-
privateKeyToAccount(key as `0x${string}`)
|
|
105
|
-
);
|
|
104
|
+
this.account = this.secureKey.use((key) => privateKeyToAccount(key as `0x${string}`));
|
|
106
105
|
} else {
|
|
107
106
|
// privateKey is a plain string - encrypt it immediately
|
|
108
107
|
const privateKeyString = config.privateKey as string;
|
|
@@ -117,9 +116,7 @@ export class WebSocketClient extends EventEmitter<SDKEvents> {
|
|
|
117
116
|
this.ownsSecureKey = true; // We created it, we own it
|
|
118
117
|
|
|
119
118
|
// Create account using the secure key
|
|
120
|
-
this.account = this.secureKey.use((key) =>
|
|
121
|
-
privateKeyToAccount(key as `0x${string}`)
|
|
122
|
-
);
|
|
119
|
+
this.account = this.secureKey.use((key) => privateKeyToAccount(key as `0x${string}`));
|
|
123
120
|
}
|
|
124
121
|
|
|
125
122
|
if (
|
|
@@ -241,6 +238,10 @@ export class WebSocketClient extends EventEmitter<SDKEvents> {
|
|
|
241
238
|
// Clear any existing connection
|
|
242
239
|
this.disconnect();
|
|
243
240
|
|
|
241
|
+
// Reset intentional disconnect flag after clearing connection
|
|
242
|
+
// This allows automatic reconnection for this new connection
|
|
243
|
+
this.intentionalDisconnect = false;
|
|
244
|
+
|
|
244
245
|
// Build connection URL with webhook parameter
|
|
245
246
|
let url = this.config.wsUrl;
|
|
246
247
|
if (this.config.webhookUrl) {
|
|
@@ -362,6 +363,9 @@ export class WebSocketClient extends EventEmitter<SDKEvents> {
|
|
|
362
363
|
public disconnect(): void {
|
|
363
364
|
this.logger.info("Disconnecting from WebSocket server");
|
|
364
365
|
|
|
366
|
+
// Mark as intentional disconnect to prevent reconnection
|
|
367
|
+
this.intentionalDisconnect = true;
|
|
368
|
+
|
|
365
369
|
// Clear all timers
|
|
366
370
|
if (this.reconnectTimer) {
|
|
367
371
|
clearTimeout(this.reconnectTimer);
|
|
@@ -795,6 +799,12 @@ export class WebSocketClient extends EventEmitter<SDKEvents> {
|
|
|
795
799
|
* Handle reconnection logic with configurable retry strategy (REL-3)
|
|
796
800
|
*/
|
|
797
801
|
private handleReconnection(): void {
|
|
802
|
+
// Don't reconnect if disconnect was intentional
|
|
803
|
+
if (this.intentionalDisconnect) {
|
|
804
|
+
this.logger.debug("Skipping reconnection - disconnect was intentional");
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
|
|
798
808
|
if (!this.config.reconnect || this.connectionState.reconnecting) {
|
|
799
809
|
return;
|
|
800
810
|
}
|
|
@@ -428,12 +428,18 @@ describe("ResponseFormatter", () => {
|
|
|
428
428
|
data: { message: "Test", code: 1 }
|
|
429
429
|
};
|
|
430
430
|
|
|
431
|
-
const rawResult = ResponseFormatter.validateAndFormat(message, {
|
|
431
|
+
const rawResult = ResponseFormatter.validateAndFormat(message, {
|
|
432
|
+
format: "raw",
|
|
433
|
+
includeMetadata: false
|
|
434
|
+
});
|
|
432
435
|
expect(rawResult.raw).toBeDefined();
|
|
433
436
|
expect(rawResult.humanized).toBeUndefined();
|
|
434
437
|
expect(rawResult.metadata).toBeUndefined();
|
|
435
438
|
|
|
436
|
-
const bothWithMeta = ResponseFormatter.validateAndFormat(message, {
|
|
439
|
+
const bothWithMeta = ResponseFormatter.validateAndFormat(message, {
|
|
440
|
+
format: "both",
|
|
441
|
+
includeMetadata: true
|
|
442
|
+
});
|
|
437
443
|
expect(bothWithMeta.raw).toBeDefined();
|
|
438
444
|
expect(bothWithMeta.humanized).toBeDefined();
|
|
439
445
|
expect(bothWithMeta.metadata).toBeDefined();
|
|
@@ -54,6 +54,7 @@ export class RegularMessageHandler extends BaseMessageHandler<UserMessage> {
|
|
|
54
54
|
contentType: message.content_type || "text/plain",
|
|
55
55
|
success: true,
|
|
56
56
|
timestamp: new Date(),
|
|
57
|
+
raw: message as any, // Include raw message for request correlation
|
|
57
58
|
humanized:
|
|
58
59
|
typeof message.content === "string" ? message.content : JSON.stringify(message.content)
|
|
59
60
|
};
|
|
@@ -118,7 +118,10 @@ describe("WebhookHandler", () => {
|
|
|
118
118
|
expect(queue[0].payload.timestamp).toBeDefined();
|
|
119
119
|
expect(queue[1].payload.timestamp).toBeDefined();
|
|
120
120
|
// Each webhook gets unique timestamp
|
|
121
|
-
expect(
|
|
121
|
+
expect(
|
|
122
|
+
queue[0].payload.timestamp !== queue[1].payload.timestamp ||
|
|
123
|
+
queue[0].payload.data.content !== queue[1].payload.data.content
|
|
124
|
+
).toBe(true);
|
|
122
125
|
});
|
|
123
126
|
|
|
124
127
|
it("should include timestamp in webhook payload", async () => {
|
|
@@ -584,20 +587,17 @@ describe("WebhookHandler", () => {
|
|
|
584
587
|
|
|
585
588
|
describe("Protocol Validation", () => {
|
|
586
589
|
it("should reject HTTP for non-localhost without allowInsecureWebhooks", () => {
|
|
587
|
-
const urls = [
|
|
588
|
-
"http://example.com/webhook"
|
|
589
|
-
];
|
|
590
|
+
const urls = ["http://example.com/webhook"];
|
|
590
591
|
|
|
591
592
|
urls.forEach((url) => {
|
|
592
593
|
expect(() => (handler as any).validateWebhookUrl(url)).toThrow(WebhookError);
|
|
593
|
-
expect(() => (handler as any).validateWebhookUrl(url)).toThrow(
|
|
594
|
+
expect(() => (handler as any).validateWebhookUrl(url)).toThrow(
|
|
595
|
+
/must use HTTPS|Only HTTPS/i
|
|
596
|
+
);
|
|
594
597
|
});
|
|
595
598
|
|
|
596
599
|
// Private IPs are blocked for different reason (private IP, not HTTPS requirement)
|
|
597
|
-
const privateUrls = [
|
|
598
|
-
"http://192.168.1.1/webhook",
|
|
599
|
-
"http://10.0.0.1/webhook"
|
|
600
|
-
];
|
|
600
|
+
const privateUrls = ["http://192.168.1.1/webhook", "http://10.0.0.1/webhook"];
|
|
601
601
|
privateUrls.forEach((url) => {
|
|
602
602
|
expect(() => (handler as any).validateWebhookUrl(url)).toThrow(WebhookError);
|
|
603
603
|
expect(() => (handler as any).validateWebhookUrl(url)).toThrow(/private IP/i);
|
|
@@ -720,7 +720,10 @@ describe("WebhookHandler", () => {
|
|
|
720
720
|
vi.advanceTimersByTime(10000);
|
|
721
721
|
}
|
|
722
722
|
|
|
723
|
-
expect(errorHandler).toHaveBeenCalledWith(
|
|
723
|
+
expect(errorHandler).toHaveBeenCalledWith(
|
|
724
|
+
expect.any(Error),
|
|
725
|
+
"https://webhook.example.com/events"
|
|
726
|
+
);
|
|
724
727
|
});
|
|
725
728
|
});
|
|
726
729
|
|
|
@@ -156,7 +156,10 @@ export class MessageRouter extends EventEmitter<SDKEvents> {
|
|
|
156
156
|
* });
|
|
157
157
|
* ```
|
|
158
158
|
*/
|
|
159
|
-
public async sendDirectCommand(
|
|
159
|
+
public async sendDirectCommand(
|
|
160
|
+
command: AgentCommand,
|
|
161
|
+
waitForResponse: boolean = false
|
|
162
|
+
): Promise<FormattedResponse | void> {
|
|
160
163
|
if (!this.wsClient.isConnected) {
|
|
161
164
|
throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
|
|
162
165
|
}
|
|
@@ -185,11 +188,21 @@ export class MessageRouter extends EventEmitter<SDKEvents> {
|
|
|
185
188
|
from: walletAddress
|
|
186
189
|
});
|
|
187
190
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
const options: SendMessageOptions = {
|
|
192
|
+
room,
|
|
193
|
+
from: walletAddress,
|
|
194
|
+
waitForResponse,
|
|
195
|
+
timeout: this.messageTimeout,
|
|
196
|
+
format: this.responseFormat
|
|
197
|
+
};
|
|
192
198
|
|
|
199
|
+
if (waitForResponse) {
|
|
200
|
+
return await this.sendMessageAndWaitForResponse(message, options);
|
|
201
|
+
} else {
|
|
202
|
+
await this.wsClient.sendMessage(message);
|
|
203
|
+
await this.webhookHandler.sendMessageWebhook(message);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
193
206
|
|
|
194
207
|
/**
|
|
195
208
|
* Send message and wait for agent response
|
|
@@ -220,16 +233,45 @@ export class MessageRouter extends EventEmitter<SDKEvents> {
|
|
|
220
233
|
|
|
221
234
|
// Wait for agent response with automatic timeout and cleanup
|
|
222
235
|
// The filter ensures we only match responses for THIS specific request
|
|
236
|
+
const requestTimestamp = Date.now();
|
|
237
|
+
let responseMatched = false;
|
|
238
|
+
|
|
223
239
|
const response = await waitForEvent<AgentResponse>(this.wsClient, "agent:response", {
|
|
224
240
|
timeout,
|
|
225
241
|
filter: (r) => {
|
|
242
|
+
// Prevent double-matching
|
|
243
|
+
if (responseMatched) return false;
|
|
244
|
+
|
|
226
245
|
// Try to match by client_request_id if server echoes it back
|
|
227
246
|
const responseRequestId =
|
|
228
247
|
r.raw?.data && "client_request_id" in r.raw.data
|
|
229
248
|
? (r.raw.data as any).client_request_id
|
|
230
249
|
: undefined;
|
|
231
250
|
|
|
232
|
-
|
|
251
|
+
if (responseRequestId === requestId) {
|
|
252
|
+
responseMatched = true;
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Fallback: If server doesn't support client_request_id,
|
|
257
|
+
// match the first response from the expected room within 60 seconds
|
|
258
|
+
// This handles servers that don't echo back client_request_id
|
|
259
|
+
const timeSinceRequest = Date.now() - requestTimestamp;
|
|
260
|
+
const responseRoom = r.raw?.room;
|
|
261
|
+
const isFromExpectedRoom = responseRoom === message.room;
|
|
262
|
+
const isWithinTimeWindow = timeSinceRequest < 60000; // 60 second window
|
|
263
|
+
|
|
264
|
+
if (isFromExpectedRoom && isWithinTimeWindow && !responseRequestId) {
|
|
265
|
+
this.logger.debug("Matching response without client_request_id (server fallback)", {
|
|
266
|
+
responseRoom,
|
|
267
|
+
expectedRoom: message.room,
|
|
268
|
+
timeSinceRequest
|
|
269
|
+
});
|
|
270
|
+
responseMatched = true;
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return false;
|
|
233
275
|
},
|
|
234
276
|
timeoutMessage: `Message timeout - no response received after ${timeout}ms (requestId: ${requestId})`
|
|
235
277
|
});
|