@teneo-protocol/sdk 1.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/.dockerignore +14 -0
- package/.env.test.example +14 -0
- package/.eslintrc.json +26 -0
- package/.github/workflows/claude-code-review.yml +78 -0
- package/.github/workflows/claude-reviewer.yml +64 -0
- package/.github/workflows/publish-npm.yml +38 -0
- package/.github/workflows/push-to-main.yml +23 -0
- package/.node-version +1 -0
- package/.prettierrc +11 -0
- package/Dockerfile +25 -0
- package/LICENCE +661 -0
- package/README.md +709 -0
- package/dist/constants.d.ts +42 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +45 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/websocket-client.d.ts +261 -0
- package/dist/core/websocket-client.d.ts.map +1 -0
- package/dist/core/websocket-client.js +875 -0
- package/dist/core/websocket-client.js.map +1 -0
- package/dist/formatters/response-formatter.d.ts +354 -0
- package/dist/formatters/response-formatter.d.ts.map +1 -0
- package/dist/formatters/response-formatter.js +575 -0
- package/dist/formatters/response-formatter.js.map +1 -0
- package/dist/handlers/message-handler-registry.d.ts +155 -0
- package/dist/handlers/message-handler-registry.d.ts.map +1 -0
- package/dist/handlers/message-handler-registry.js +216 -0
- package/dist/handlers/message-handler-registry.js.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts +112 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js +40 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.js +25 -0
- package/dist/handlers/message-handlers/agents-list-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts +71 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.js +30 -0
- package/dist/handlers/message-handlers/auth-error-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.js +60 -0
- package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.js +23 -0
- package/dist/handlers/message-handlers/auth-required-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.js +51 -0
- package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -0
- package/dist/handlers/message-handlers/base-handler.d.ts +55 -0
- package/dist/handlers/message-handlers/base-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/base-handler.js +83 -0
- package/dist/handlers/message-handlers/base-handler.js.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts +73 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.js +47 -0
- package/dist/handlers/message-handlers/challenge-handler.js.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.js +29 -0
- package/dist/handlers/message-handlers/error-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/index.d.ts +28 -0
- package/dist/handlers/message-handlers/index.d.ts.map +1 -0
- package/dist/handlers/message-handlers/index.js +100 -0
- package/dist/handlers/message-handlers/index.js.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +122 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js +30 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts +104 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js +36 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts +56 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.js +59 -0
- package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.js +44 -0
- package/dist/handlers/message-handlers/task-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/types.d.ts +51 -0
- package/dist/handlers/message-handlers/types.d.ts.map +1 -0
- package/dist/handlers/message-handlers/types.js +7 -0
- package/dist/handlers/message-handlers/types.js.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js.map +1 -0
- package/dist/handlers/webhook-handler.d.ts +202 -0
- package/dist/handlers/webhook-handler.d.ts.map +1 -0
- package/dist/handlers/webhook-handler.js +511 -0
- package/dist/handlers/webhook-handler.js.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +217 -0
- package/dist/index.js.map +1 -0
- package/dist/managers/agent-registry.d.ts +173 -0
- package/dist/managers/agent-registry.d.ts.map +1 -0
- package/dist/managers/agent-registry.js +310 -0
- package/dist/managers/agent-registry.js.map +1 -0
- package/dist/managers/connection-manager.d.ts +134 -0
- package/dist/managers/connection-manager.d.ts.map +1 -0
- package/dist/managers/connection-manager.js +176 -0
- package/dist/managers/connection-manager.js.map +1 -0
- package/dist/managers/index.d.ts +9 -0
- package/dist/managers/index.d.ts.map +1 -0
- package/dist/managers/index.js +16 -0
- package/dist/managers/index.js.map +1 -0
- package/dist/managers/message-router.d.ts +112 -0
- package/dist/managers/message-router.d.ts.map +1 -0
- package/dist/managers/message-router.js +260 -0
- package/dist/managers/message-router.js.map +1 -0
- package/dist/managers/room-manager.d.ts +165 -0
- package/dist/managers/room-manager.d.ts.map +1 -0
- package/dist/managers/room-manager.js +227 -0
- package/dist/managers/room-manager.js.map +1 -0
- package/dist/teneo-sdk.d.ts +703 -0
- package/dist/teneo-sdk.d.ts.map +1 -0
- package/dist/teneo-sdk.js +907 -0
- package/dist/teneo-sdk.js.map +1 -0
- package/dist/types/config.d.ts +1047 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +720 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/error-codes.d.ts +29 -0
- package/dist/types/error-codes.d.ts.map +1 -0
- package/dist/types/error-codes.js +41 -0
- package/dist/types/error-codes.js.map +1 -0
- package/dist/types/events.d.ts +616 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +261 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/health.d.ts +40 -0
- package/dist/types/health.d.ts.map +1 -0
- package/dist/types/health.js +6 -0
- package/dist/types/health.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +123 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/messages.d.ts +3734 -0
- package/dist/types/messages.d.ts.map +1 -0
- package/dist/types/messages.js +482 -0
- package/dist/types/messages.js.map +1 -0
- package/dist/types/validation.d.ts +81 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/dist/types/validation.js +115 -0
- package/dist/types/validation.js.map +1 -0
- package/dist/utils/bounded-queue.d.ts +127 -0
- package/dist/utils/bounded-queue.d.ts.map +1 -0
- package/dist/utils/bounded-queue.js +181 -0
- package/dist/utils/bounded-queue.js.map +1 -0
- package/dist/utils/circuit-breaker.d.ts +141 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +215 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/deduplication-cache.d.ts +110 -0
- package/dist/utils/deduplication-cache.d.ts.map +1 -0
- package/dist/utils/deduplication-cache.js +177 -0
- package/dist/utils/deduplication-cache.js.map +1 -0
- package/dist/utils/event-waiter.d.ts +101 -0
- package/dist/utils/event-waiter.d.ts.map +1 -0
- package/dist/utils/event-waiter.js +118 -0
- package/dist/utils/event-waiter.js.map +1 -0
- package/dist/utils/index.d.ts +51 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +72 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +22 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +91 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +122 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +190 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/retry-policy.d.ts +191 -0
- package/dist/utils/retry-policy.d.ts.map +1 -0
- package/dist/utils/retry-policy.js +225 -0
- package/dist/utils/retry-policy.js.map +1 -0
- package/dist/utils/secure-private-key.d.ts +113 -0
- package/dist/utils/secure-private-key.d.ts.map +1 -0
- package/dist/utils/secure-private-key.js +188 -0
- package/dist/utils/secure-private-key.js.map +1 -0
- package/dist/utils/signature-verifier.d.ts +143 -0
- package/dist/utils/signature-verifier.d.ts.map +1 -0
- package/dist/utils/signature-verifier.js +238 -0
- package/dist/utils/signature-verifier.js.map +1 -0
- package/dist/utils/ssrf-validator.d.ts +36 -0
- package/dist/utils/ssrf-validator.d.ts.map +1 -0
- package/dist/utils/ssrf-validator.js +195 -0
- package/dist/utils/ssrf-validator.js.map +1 -0
- package/examples/.env.example +17 -0
- package/examples/basic-usage.ts +211 -0
- package/examples/production-dashboard/.env.example +153 -0
- package/examples/production-dashboard/package.json +39 -0
- package/examples/production-dashboard/public/dashboard.html +642 -0
- package/examples/production-dashboard/server.ts +753 -0
- package/examples/webhook-integration.ts +239 -0
- package/examples/x-influencer-battle-redesign.html +1065 -0
- package/examples/x-influencer-battle-server.ts +217 -0
- package/examples/x-influencer-battle.html +787 -0
- package/package.json +65 -0
- package/src/constants.ts +43 -0
- package/src/core/websocket-client.test.ts +512 -0
- package/src/core/websocket-client.ts +1056 -0
- package/src/formatters/response-formatter.test.ts +571 -0
- package/src/formatters/response-formatter.ts +677 -0
- package/src/handlers/message-handler-registry.ts +239 -0
- package/src/handlers/message-handlers/agent-selected-handler.ts +40 -0
- package/src/handlers/message-handlers/agents-list-handler.ts +26 -0
- package/src/handlers/message-handlers/auth-error-handler.ts +31 -0
- package/src/handlers/message-handlers/auth-message-handler.ts +66 -0
- package/src/handlers/message-handlers/auth-required-handler.ts +23 -0
- package/src/handlers/message-handlers/auth-success-handler.ts +57 -0
- package/src/handlers/message-handlers/base-handler.ts +101 -0
- package/src/handlers/message-handlers/challenge-handler.ts +57 -0
- package/src/handlers/message-handlers/error-message-handler.ts +27 -0
- package/src/handlers/message-handlers/index.ts +77 -0
- package/src/handlers/message-handlers/list-rooms-response-handler.ts +28 -0
- package/src/handlers/message-handlers/ping-pong-handler.ts +30 -0
- package/src/handlers/message-handlers/regular-message-handler.ts +65 -0
- package/src/handlers/message-handlers/subscribe-response-handler.ts +47 -0
- package/src/handlers/message-handlers/task-response-handler.ts +45 -0
- package/src/handlers/message-handlers/types.ts +77 -0
- package/src/handlers/message-handlers/unsubscribe-response-handler.ts +47 -0
- package/src/handlers/webhook-handler.test.ts +789 -0
- package/src/handlers/webhook-handler.ts +576 -0
- package/src/index.ts +269 -0
- package/src/managers/agent-registry.test.ts +466 -0
- package/src/managers/agent-registry.ts +347 -0
- package/src/managers/connection-manager.ts +195 -0
- package/src/managers/index.ts +9 -0
- package/src/managers/message-router.ts +349 -0
- package/src/managers/room-manager.ts +248 -0
- package/src/teneo-sdk.ts +1022 -0
- package/src/types/config.test.ts +325 -0
- package/src/types/config.ts +799 -0
- package/src/types/error-codes.ts +44 -0
- package/src/types/events.test.ts +302 -0
- package/src/types/events.ts +382 -0
- package/src/types/health.ts +46 -0
- package/src/types/index.ts +199 -0
- package/src/types/messages.test.ts +660 -0
- package/src/types/messages.ts +570 -0
- package/src/types/validation.ts +123 -0
- package/src/utils/bounded-queue.test.ts +356 -0
- package/src/utils/bounded-queue.ts +205 -0
- package/src/utils/circuit-breaker.test.ts +394 -0
- package/src/utils/circuit-breaker.ts +262 -0
- package/src/utils/deduplication-cache.test.ts +380 -0
- package/src/utils/deduplication-cache.ts +198 -0
- package/src/utils/event-waiter.test.ts +381 -0
- package/src/utils/event-waiter.ts +172 -0
- package/src/utils/index.ts +74 -0
- package/src/utils/logger.ts +87 -0
- package/src/utils/rate-limiter.test.ts +341 -0
- package/src/utils/rate-limiter.ts +211 -0
- package/src/utils/retry-policy.test.ts +558 -0
- package/src/utils/retry-policy.ts +272 -0
- package/src/utils/secure-private-key.test.ts +356 -0
- package/src/utils/secure-private-key.ts +205 -0
- package/src/utils/signature-verifier.test.ts +464 -0
- package/src/utils/signature-verifier.ts +298 -0
- package/src/utils/ssrf-validator.test.ts +372 -0
- package/src/utils/ssrf-validator.ts +224 -0
- package/tests/integration/real-server.test.ts +740 -0
- package/tests/integration/websocket.test.ts +381 -0
- package/tests/integration-setup.ts +16 -0
- package/tests/setup.ts +34 -0
- package/tsconfig.json +32 -0
- package/vitest.config.ts +42 -0
- package/vitest.integration.config.ts +23 -0
|
@@ -0,0 +1,799 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration schemas for Teneo Protocol SDK using Zod
|
|
3
|
+
* Provides runtime validation and TypeScript type inference
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { ClientTypeSchema, RoomSchema, MessageTypeSchema, MessageType } from "./messages";
|
|
8
|
+
import { RetryStrategySchema, type RetryStrategy } from "../utils/retry-policy";
|
|
9
|
+
import type { SecurePrivateKey } from "../utils/secure-private-key";
|
|
10
|
+
|
|
11
|
+
// Logger interface
|
|
12
|
+
export interface Logger {
|
|
13
|
+
debug: (message: string, data?: any) => void;
|
|
14
|
+
info: (message: string, data?: any) => void;
|
|
15
|
+
warn: (message: string, data?: any) => void;
|
|
16
|
+
error: (message: string, data?: any) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Logger schema - using loose function validation
|
|
20
|
+
export const LoggerSchema = z.object({
|
|
21
|
+
debug: z.function(),
|
|
22
|
+
info: z.function(),
|
|
23
|
+
warn: z.function(),
|
|
24
|
+
error: z.function()
|
|
25
|
+
}) as z.ZodType<Logger>;
|
|
26
|
+
|
|
27
|
+
// Log level schema
|
|
28
|
+
export const LogLevelSchema = z.enum(["debug", "info", "warn", "error", "silent"]);
|
|
29
|
+
|
|
30
|
+
// Response format schema
|
|
31
|
+
export const ResponseFormatSchema = z.enum(["raw", "humanized", "both"]);
|
|
32
|
+
|
|
33
|
+
// Webhook event type schema
|
|
34
|
+
export const WebhookEventTypeSchema = z.enum([
|
|
35
|
+
"message",
|
|
36
|
+
"task",
|
|
37
|
+
"task_response",
|
|
38
|
+
"agent_selected",
|
|
39
|
+
"error",
|
|
40
|
+
"connection_state",
|
|
41
|
+
"auth_state"
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
// Custom Zod schema for SecurePrivateKey or string
|
|
45
|
+
const PrivateKeySchema = z.union([
|
|
46
|
+
z.string(),
|
|
47
|
+
z.custom<SecurePrivateKey>(
|
|
48
|
+
(val) => {
|
|
49
|
+
// Check if it's a SecurePrivateKey instance
|
|
50
|
+
return val && typeof val === 'object' && 'use' in val && 'destroy' in val && 'isDestroyed' in val;
|
|
51
|
+
},
|
|
52
|
+
{ message: "Must be a string or SecurePrivateKey instance" }
|
|
53
|
+
)
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
// SDK Configuration schema
|
|
57
|
+
export const SDKConfigSchema = z.object({
|
|
58
|
+
// WebSocket configuration
|
|
59
|
+
wsUrl: z
|
|
60
|
+
.string()
|
|
61
|
+
.url()
|
|
62
|
+
.refine((url) => url.startsWith("ws://") || url.startsWith("wss://"), {
|
|
63
|
+
message: "WebSocket URL must start with ws:// or wss://"
|
|
64
|
+
}),
|
|
65
|
+
|
|
66
|
+
// Authentication
|
|
67
|
+
privateKey: PrivateKeySchema.optional(),
|
|
68
|
+
walletAddress: z.string().optional(),
|
|
69
|
+
|
|
70
|
+
// Client identification
|
|
71
|
+
clientType: ClientTypeSchema.optional(),
|
|
72
|
+
clientName: z.string().optional(),
|
|
73
|
+
|
|
74
|
+
// Room configuration
|
|
75
|
+
autoJoinRooms: z.array(z.string()).optional(),
|
|
76
|
+
|
|
77
|
+
// Webhook configuration
|
|
78
|
+
webhookUrl: z.string().url().optional(),
|
|
79
|
+
webhookHeaders: z.record(z.string()).optional(),
|
|
80
|
+
webhookRetries: z.number().min(0).max(10).optional(),
|
|
81
|
+
webhookTimeout: z.number().min(1000).max(60000).optional(),
|
|
82
|
+
webhookRetryStrategy: RetryStrategySchema.optional(), // REL-3: Configurable retry strategy
|
|
83
|
+
|
|
84
|
+
// Connection settings
|
|
85
|
+
reconnect: z.boolean().optional(),
|
|
86
|
+
reconnectDelay: z.number().min(100).max(60000).optional(),
|
|
87
|
+
maxReconnectAttempts: z.number().min(0).max(100).optional(),
|
|
88
|
+
connectionTimeout: z.number().min(1000).max(120000).optional(),
|
|
89
|
+
reconnectStrategy: RetryStrategySchema.optional(), // REL-3: Configurable retry strategy
|
|
90
|
+
|
|
91
|
+
// Message settings
|
|
92
|
+
messageTimeout: z.number().min(1000).max(300000).optional(),
|
|
93
|
+
maxMessageSize: z.number().min(1024).max(10485760).optional(), // 1KB to 10MB
|
|
94
|
+
maxMessagesPerSecond: z.number().min(1).max(1000).optional(), // Rate limiting
|
|
95
|
+
|
|
96
|
+
// Response formatting
|
|
97
|
+
responseFormat: ResponseFormatSchema.optional(),
|
|
98
|
+
includeMetadata: z.boolean().optional(),
|
|
99
|
+
|
|
100
|
+
// Logging
|
|
101
|
+
logLevel: LogLevelSchema.optional(),
|
|
102
|
+
logger: LoggerSchema.optional(),
|
|
103
|
+
|
|
104
|
+
// Performance
|
|
105
|
+
enableCache: z.boolean().optional(),
|
|
106
|
+
cacheTimeout: z.number().min(1000).max(3600000).optional(),
|
|
107
|
+
maxCacheSize: z.number().min(1).max(10000).optional(),
|
|
108
|
+
|
|
109
|
+
// Security
|
|
110
|
+
validateSignatures: z.boolean().optional(),
|
|
111
|
+
trustedAgentAddresses: z.array(z.string()).optional(),
|
|
112
|
+
requireSignaturesFor: z.array(MessageTypeSchema).optional(),
|
|
113
|
+
strictSignatureValidation: z.boolean().optional(),
|
|
114
|
+
allowInsecureWebhooks: z.boolean().optional(),
|
|
115
|
+
|
|
116
|
+
// Message deduplication (CB-4)
|
|
117
|
+
enableMessageDeduplication: z.boolean().optional(),
|
|
118
|
+
messageDedupeTtl: z.number().min(1000).max(3600000).optional(), // 1s to 1 hour
|
|
119
|
+
messageDedupMaxSize: z.number().min(1).max(100000).optional()
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Partial config for constructor
|
|
123
|
+
export const PartialSDKConfigSchema = SDKConfigSchema.partial().refine(
|
|
124
|
+
(config) => config.wsUrl !== undefined,
|
|
125
|
+
{ message: "WebSocket URL is required" }
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Connection state schema
|
|
129
|
+
export const ConnectionStateSchema = z.object({
|
|
130
|
+
connected: z.boolean(),
|
|
131
|
+
authenticated: z.boolean(),
|
|
132
|
+
reconnecting: z.boolean(),
|
|
133
|
+
reconnectAttempts: z.number(),
|
|
134
|
+
lastError: z.instanceof(Error).optional(),
|
|
135
|
+
lastConnectedAt: z.date().optional(),
|
|
136
|
+
lastDisconnectedAt: z.date().optional()
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Authentication state schema
|
|
140
|
+
export const AuthenticationStateSchema = z.object({
|
|
141
|
+
authenticated: z.boolean(),
|
|
142
|
+
clientId: z.string().optional(),
|
|
143
|
+
walletAddress: z.string().optional(),
|
|
144
|
+
isWhitelisted: z.boolean().optional(),
|
|
145
|
+
isAdmin: z.boolean().optional(),
|
|
146
|
+
nftVerified: z.boolean().optional(),
|
|
147
|
+
rooms: z.array(z.string()).optional(), // Room IDs for backward compatibility
|
|
148
|
+
roomObjects: z.array(RoomSchema).optional(), // Full room objects from auth
|
|
149
|
+
privateRoomId: z.string().optional(),
|
|
150
|
+
challenge: z.string().optional(),
|
|
151
|
+
challengeTimestamp: z.number().optional()
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Webhook config schema
|
|
155
|
+
export const WebhookConfigSchema = z.object({
|
|
156
|
+
url: z.string().url(),
|
|
157
|
+
headers: z.record(z.string()).optional(),
|
|
158
|
+
retries: z.number().min(0).max(10).optional(),
|
|
159
|
+
timeout: z.number().min(1000).max(60000).optional(),
|
|
160
|
+
events: z.array(WebhookEventTypeSchema).optional()
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Webhook payload schema
|
|
164
|
+
export const WebhookPayloadSchema = z.object({
|
|
165
|
+
event: WebhookEventTypeSchema,
|
|
166
|
+
timestamp: z.string(),
|
|
167
|
+
data: z.any(),
|
|
168
|
+
metadata: z
|
|
169
|
+
.object({
|
|
170
|
+
clientId: z.string().optional(),
|
|
171
|
+
roomId: z.string().optional(),
|
|
172
|
+
agentId: z.string().optional(),
|
|
173
|
+
taskId: z.string().optional()
|
|
174
|
+
})
|
|
175
|
+
.optional()
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Type inference from schemas
|
|
179
|
+
export type LogLevel = z.infer<typeof LogLevelSchema>;
|
|
180
|
+
export type ResponseFormat = z.infer<typeof ResponseFormatSchema>;
|
|
181
|
+
export type WebhookEventType = z.infer<typeof WebhookEventTypeSchema>;
|
|
182
|
+
export type SDKConfig = z.infer<typeof SDKConfigSchema>;
|
|
183
|
+
export type PartialSDKConfig = z.infer<typeof PartialSDKConfigSchema>;
|
|
184
|
+
export type ConnectionState = z.infer<typeof ConnectionStateSchema>;
|
|
185
|
+
export type AuthenticationState = z.infer<typeof AuthenticationStateSchema>;
|
|
186
|
+
export type WebhookConfig = z.infer<typeof WebhookConfigSchema>;
|
|
187
|
+
export type WebhookPayload = z.infer<typeof WebhookPayloadSchema>;
|
|
188
|
+
|
|
189
|
+
// Re-export RetryStrategy for convenience
|
|
190
|
+
export type { RetryStrategy };
|
|
191
|
+
|
|
192
|
+
// Default configuration with validation
|
|
193
|
+
export const DEFAULT_CONFIG: PartialSDKConfig = SDKConfigSchema.partial().parse({
|
|
194
|
+
wsUrl: "ws://localhost:8080/ws",
|
|
195
|
+
clientType: "user",
|
|
196
|
+
reconnect: true,
|
|
197
|
+
reconnectDelay: 5000,
|
|
198
|
+
maxReconnectAttempts: 10,
|
|
199
|
+
connectionTimeout: 30000,
|
|
200
|
+
messageTimeout: 30000,
|
|
201
|
+
maxMessageSize: 2 * 1024 * 1024, // 2MB
|
|
202
|
+
maxMessagesPerSecond: 10, // Rate limit: 10 messages per second
|
|
203
|
+
responseFormat: "humanized",
|
|
204
|
+
includeMetadata: false,
|
|
205
|
+
logLevel: "info",
|
|
206
|
+
enableCache: true,
|
|
207
|
+
cacheTimeout: 300000, // 5 minutes
|
|
208
|
+
maxCacheSize: 100,
|
|
209
|
+
validateSignatures: false,
|
|
210
|
+
trustedAgentAddresses: [],
|
|
211
|
+
requireSignaturesFor: ["task_response", "agent_selected"],
|
|
212
|
+
strictSignatureValidation: false,
|
|
213
|
+
allowInsecureWebhooks: false,
|
|
214
|
+
webhookRetries: 3,
|
|
215
|
+
webhookTimeout: 10000,
|
|
216
|
+
enableMessageDeduplication: true, // Enable by default to prevent duplicates
|
|
217
|
+
messageDedupeTtl: 60000, // 60 seconds (1 minute)
|
|
218
|
+
messageDedupMaxSize: 10000 // 10k messages
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Configuration validation with custom refinements
|
|
222
|
+
export function validateConfig(config: unknown): SDKConfig {
|
|
223
|
+
// First validate basic structure
|
|
224
|
+
const parsed = SDKConfigSchema.parse(config);
|
|
225
|
+
|
|
226
|
+
// Additional custom validations
|
|
227
|
+
if (parsed.webhookUrl && !parsed.allowInsecureWebhooks) {
|
|
228
|
+
const url = new URL(parsed.webhookUrl);
|
|
229
|
+
if (url.protocol === "http:" && !["localhost", "127.0.0.1", "::1"].includes(url.hostname)) {
|
|
230
|
+
throw new Error("Webhook URL must use HTTPS for non-localhost endpoints");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return parsed;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Safe parse configuration
|
|
238
|
+
export function safeParseConfig(config: unknown): {
|
|
239
|
+
success: boolean;
|
|
240
|
+
data?: SDKConfig;
|
|
241
|
+
error?: z.ZodError | Error;
|
|
242
|
+
} {
|
|
243
|
+
try {
|
|
244
|
+
const data = validateConfig(config);
|
|
245
|
+
return { success: true, data };
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (error instanceof z.ZodError) {
|
|
248
|
+
return { success: false, error };
|
|
249
|
+
}
|
|
250
|
+
return { success: false, error: error as Error };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Fluent API builder for creating Teneo SDK configurations with validation.
|
|
256
|
+
* Provides a chainable interface for configuring the SDK with runtime validation
|
|
257
|
+
* at each step. Call `.build()` to create the final validated configuration.
|
|
258
|
+
*
|
|
259
|
+
* This is the recommended way to configure the SDK for complex setups, as it provides
|
|
260
|
+
* better IDE intellisense, method chaining, and validates each configuration option
|
|
261
|
+
* as you set it.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```typescript
|
|
265
|
+
* // Basic configuration
|
|
266
|
+
* const config = new SDKConfigBuilder()
|
|
267
|
+
* .withWebSocketUrl('wss://teneo.example.com')
|
|
268
|
+
* .withAuthentication('0x...')
|
|
269
|
+
* .build();
|
|
270
|
+
*
|
|
271
|
+
* const sdk = new TeneoSDK(config);
|
|
272
|
+
*
|
|
273
|
+
* // Full configuration with all options
|
|
274
|
+
* const config = new SDKConfigBuilder()
|
|
275
|
+
* .withWebSocketUrl('wss://teneo.example.com')
|
|
276
|
+
* .withAuthentication('0x...', '0xYourWalletAddress')
|
|
277
|
+
* .withAutoJoinRooms(['general', 'announcements'])
|
|
278
|
+
* .withWebhook('https://api.example.com/webhooks', {
|
|
279
|
+
* 'Authorization': 'Bearer token'
|
|
280
|
+
* })
|
|
281
|
+
* .withReconnection(true, 5000, 10)
|
|
282
|
+
* .withResponseFormat({ format: 'both', includeMetadata: true })
|
|
283
|
+
* .withLogging('debug')
|
|
284
|
+
* .withCache(true, 300000, 100)
|
|
285
|
+
* .build();
|
|
286
|
+
*
|
|
287
|
+
* const sdk = new TeneoSDK(config);
|
|
288
|
+
*
|
|
289
|
+
* // Using via TeneoSDK.builder() (recommended)
|
|
290
|
+
* const sdk = TeneoSDK.builder()
|
|
291
|
+
* .withWebSocketUrl('wss://teneo.example.com')
|
|
292
|
+
* .withAuthentication('0x...')
|
|
293
|
+
* .withAutoJoinRooms(['general'])
|
|
294
|
+
* .build();
|
|
295
|
+
* ```
|
|
296
|
+
*
|
|
297
|
+
* @see {@link TeneoSDK} for the main SDK class
|
|
298
|
+
* @see {@link TeneoSDK.builder} for creating a builder instance
|
|
299
|
+
*/
|
|
300
|
+
export class SDKConfigBuilder {
|
|
301
|
+
private config: PartialSDKConfig = { ...DEFAULT_CONFIG };
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Sets the WebSocket URL for connecting to the Teneo network.
|
|
305
|
+
* URL must start with 'ws://' or 'wss://'. HTTPS (wss://) is recommended for production.
|
|
306
|
+
*
|
|
307
|
+
* @param url - WebSocket URL (e.g., 'wss://teneo.example.com')
|
|
308
|
+
* @returns this builder for method chaining
|
|
309
|
+
* @throws {z.ZodError} If URL is invalid or doesn't start with ws:// or wss://
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* builder.withWebSocketUrl('wss://teneo.example.com')
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
withWebSocketUrl(url: string): this {
|
|
317
|
+
const parsed = z
|
|
318
|
+
.string()
|
|
319
|
+
.url()
|
|
320
|
+
.refine((u) => u.startsWith("ws://") || u.startsWith("wss://"), {
|
|
321
|
+
message: "WebSocket URL must start with ws:// or wss://"
|
|
322
|
+
})
|
|
323
|
+
.parse(url);
|
|
324
|
+
this.config.wsUrl = parsed;
|
|
325
|
+
return this;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Configures Ethereum wallet-based authentication credentials.
|
|
330
|
+
* Private key is used to sign authentication challenges from the server.
|
|
331
|
+
* Wallet address is optional and will be derived from the private key if not provided.
|
|
332
|
+
*
|
|
333
|
+
* For enhanced security (SEC-3), you can pass a SecurePrivateKey instance to keep
|
|
334
|
+
* the private key encrypted in memory from the start.
|
|
335
|
+
*
|
|
336
|
+
* @param privateKey - Ethereum private key (hex string starting with 0x) or SecurePrivateKey instance
|
|
337
|
+
* @param walletAddress - Optional wallet address (will be derived if not provided)
|
|
338
|
+
* @returns this builder for method chaining
|
|
339
|
+
* @throws {z.ZodError} If privateKey or walletAddress is invalid
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```typescript
|
|
343
|
+
* // With private key string only (address derived automatically)
|
|
344
|
+
* builder.withAuthentication('0x...')
|
|
345
|
+
*
|
|
346
|
+
* // With explicit wallet address
|
|
347
|
+
* builder.withAuthentication('0x...privatekey', '0x...address')
|
|
348
|
+
*
|
|
349
|
+
* // With SecurePrivateKey for enhanced security (SEC-3)
|
|
350
|
+
* const secureKey = new SecurePrivateKey('0x...');
|
|
351
|
+
* builder.withAuthentication(secureKey, '0x...address')
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
354
|
+
withAuthentication(privateKey: string | SecurePrivateKey, walletAddress?: string): this {
|
|
355
|
+
this.config.privateKey = PrivateKeySchema.parse(privateKey);
|
|
356
|
+
if (walletAddress) {
|
|
357
|
+
this.config.walletAddress = z.string().parse(walletAddress);
|
|
358
|
+
}
|
|
359
|
+
return this;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Configures webhook URL and optional HTTP headers for receiving real-time event notifications.
|
|
364
|
+
* Webhook URL must use HTTPS for non-localhost endpoints (security requirement).
|
|
365
|
+
* Events are sent via HTTP POST requests with JSON payloads.
|
|
366
|
+
*
|
|
367
|
+
* @param url - Webhook endpoint URL (must be HTTPS unless localhost)
|
|
368
|
+
* @param headers - Optional HTTP headers to include with webhook requests (e.g., Authorization)
|
|
369
|
+
* @returns this builder for method chaining
|
|
370
|
+
* @throws {z.ZodError} If URL is invalid
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```typescript
|
|
374
|
+
* // Basic webhook
|
|
375
|
+
* builder.withWebhook('https://api.example.com/webhooks/teneo')
|
|
376
|
+
*
|
|
377
|
+
* // With authentication headers
|
|
378
|
+
* builder.withWebhook('https://api.example.com/webhooks', {
|
|
379
|
+
* 'Authorization': 'Bearer your-token',
|
|
380
|
+
* 'X-Custom-Header': 'value'
|
|
381
|
+
* })
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
384
|
+
withWebhook(url: string, headers?: Record<string, string>): this {
|
|
385
|
+
this.config.webhookUrl = z.string().url().parse(url);
|
|
386
|
+
if (headers) {
|
|
387
|
+
this.config.webhookHeaders = z.record(z.string()).parse(headers);
|
|
388
|
+
}
|
|
389
|
+
return this;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Configures rooms to automatically subscribe to after authentication.
|
|
394
|
+
* These rooms will be subscribed to automatically when connection is established.
|
|
395
|
+
*
|
|
396
|
+
* @param rooms - Array of room IDs to auto-subscribe to on connection
|
|
397
|
+
* @returns this builder for method chaining
|
|
398
|
+
* @throws {z.ZodError} If room IDs are invalid
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```typescript
|
|
402
|
+
* builder.withAutoJoinRooms(['general', 'announcements', 'support'])
|
|
403
|
+
* ```
|
|
404
|
+
*/
|
|
405
|
+
withAutoJoinRooms(rooms: string[]): this {
|
|
406
|
+
this.config.autoJoinRooms = z.array(z.string()).parse(rooms);
|
|
407
|
+
return this;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Configures automatic reconnection behavior for WebSocket connections.
|
|
412
|
+
* When enabled, SDK will automatically attempt to reconnect on disconnection.
|
|
413
|
+
* Uses exponential backoff strategy for reconnection attempts.
|
|
414
|
+
*
|
|
415
|
+
* @param optionsOrEnabled - Reconnection configuration options object, or boolean for backwards compatibility
|
|
416
|
+
* @param delay - (Deprecated positional arg) Delay between reconnection attempts in ms
|
|
417
|
+
* @param maxAttempts - (Deprecated positional arg) Maximum reconnection attempts
|
|
418
|
+
* @returns this builder for method chaining
|
|
419
|
+
* @throws {z.ZodError} If options are out of valid range
|
|
420
|
+
*
|
|
421
|
+
* @example
|
|
422
|
+
* ```typescript
|
|
423
|
+
* // New API with object (recommended)
|
|
424
|
+
* builder.withReconnection({ enabled: true })
|
|
425
|
+
* builder.withReconnection({
|
|
426
|
+
* enabled: true,
|
|
427
|
+
* delay: 3000,
|
|
428
|
+
* maxAttempts: 5
|
|
429
|
+
* })
|
|
430
|
+
*
|
|
431
|
+
* // Old API with positional args (backwards compatible)
|
|
432
|
+
* builder.withReconnection(true, 3000, 5)
|
|
433
|
+
* ```
|
|
434
|
+
*/
|
|
435
|
+
withReconnection(
|
|
436
|
+
optionsOrEnabled: { enabled?: boolean; delay?: number; maxAttempts?: number } | boolean,
|
|
437
|
+
delay?: number,
|
|
438
|
+
maxAttempts?: number
|
|
439
|
+
): this {
|
|
440
|
+
// Handle backwards compatible positional arguments
|
|
441
|
+
if (typeof optionsOrEnabled === "boolean") {
|
|
442
|
+
this.config.reconnect = z.boolean().parse(optionsOrEnabled);
|
|
443
|
+
if (delay !== undefined) {
|
|
444
|
+
this.config.reconnectDelay = z.number().min(100).max(60000).parse(delay);
|
|
445
|
+
}
|
|
446
|
+
if (maxAttempts !== undefined) {
|
|
447
|
+
this.config.maxReconnectAttempts = z.number().min(0).max(100).parse(maxAttempts);
|
|
448
|
+
}
|
|
449
|
+
} else {
|
|
450
|
+
// New object API
|
|
451
|
+
const options = optionsOrEnabled;
|
|
452
|
+
if (options.enabled !== undefined) {
|
|
453
|
+
this.config.reconnect = z.boolean().parse(options.enabled);
|
|
454
|
+
}
|
|
455
|
+
if (options.delay !== undefined) {
|
|
456
|
+
this.config.reconnectDelay = z.number().min(100).max(60000).parse(options.delay);
|
|
457
|
+
}
|
|
458
|
+
if (options.maxAttempts !== undefined) {
|
|
459
|
+
this.config.maxReconnectAttempts = z.number().min(0).max(100).parse(options.maxAttempts);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return this;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Configures how agent responses are formatted when received.
|
|
467
|
+
* Choose between raw JSON, human-readable text, or both formats.
|
|
468
|
+
* Metadata includes timestamps, agent info, and other contextual data.
|
|
469
|
+
*
|
|
470
|
+
* @param optionsOrFormat - Response format configuration object, or format string for backwards compatibility
|
|
471
|
+
* @param includeMetadata - (Deprecated positional arg) Include metadata in responses
|
|
472
|
+
* @returns this builder for method chaining
|
|
473
|
+
* @throws {z.ZodError} If format is invalid
|
|
474
|
+
*
|
|
475
|
+
* @example
|
|
476
|
+
* ```typescript
|
|
477
|
+
* // New API with object (recommended)
|
|
478
|
+
* builder.withResponseFormat({ format: 'humanized' })
|
|
479
|
+
* builder.withResponseFormat({
|
|
480
|
+
* format: 'both',
|
|
481
|
+
* includeMetadata: true
|
|
482
|
+
* })
|
|
483
|
+
*
|
|
484
|
+
* // Old API with positional args (backwards compatible)
|
|
485
|
+
* builder.withResponseFormat('humanized', true)
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
withResponseFormat(
|
|
489
|
+
optionsOrFormat: { format?: ResponseFormat; includeMetadata?: boolean } | ResponseFormat,
|
|
490
|
+
includeMetadata?: boolean
|
|
491
|
+
): this {
|
|
492
|
+
// Handle backwards compatible positional arguments
|
|
493
|
+
if (typeof optionsOrFormat === "string") {
|
|
494
|
+
this.config.responseFormat = ResponseFormatSchema.parse(optionsOrFormat);
|
|
495
|
+
if (includeMetadata !== undefined) {
|
|
496
|
+
this.config.includeMetadata = z.boolean().parse(includeMetadata);
|
|
497
|
+
}
|
|
498
|
+
} else {
|
|
499
|
+
// New object API
|
|
500
|
+
const options = optionsOrFormat;
|
|
501
|
+
if (options.format !== undefined) {
|
|
502
|
+
this.config.responseFormat = ResponseFormatSchema.parse(options.format);
|
|
503
|
+
}
|
|
504
|
+
if (options.includeMetadata !== undefined) {
|
|
505
|
+
this.config.includeMetadata = z.boolean().parse(options.includeMetadata);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return this;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Configures logging level and optionally provides a custom logger implementation.
|
|
513
|
+
* Default logger uses pino with pretty printing in development and JSON in production.
|
|
514
|
+
* Custom logger must implement the Logger interface (debug, info, warn, error methods).
|
|
515
|
+
*
|
|
516
|
+
* @param level - Log level: 'debug', 'info', 'warn', 'error', or 'silent' (default: 'info')
|
|
517
|
+
* @param logger - Optional custom logger implementation
|
|
518
|
+
* @returns this builder for method chaining
|
|
519
|
+
* @throws {z.ZodError} If level is invalid or logger doesn't implement required interface
|
|
520
|
+
*
|
|
521
|
+
* @example
|
|
522
|
+
* ```typescript
|
|
523
|
+
* // Set log level only (uses default pino logger)
|
|
524
|
+
* builder.withLogging('debug')
|
|
525
|
+
*
|
|
526
|
+
* // With custom logger
|
|
527
|
+
* const customLogger = {
|
|
528
|
+
* debug: (msg, data) => console.debug(msg, data),
|
|
529
|
+
* info: (msg, data) => console.info(msg, data),
|
|
530
|
+
* warn: (msg, data) => console.warn(msg, data),
|
|
531
|
+
* error: (msg, data) => console.error(msg, data)
|
|
532
|
+
* };
|
|
533
|
+
* builder.withLogging('info', customLogger)
|
|
534
|
+
*
|
|
535
|
+
* // Silent mode (no logs)
|
|
536
|
+
* builder.withLogging('silent')
|
|
537
|
+
* ```
|
|
538
|
+
*/
|
|
539
|
+
withLogging(level: LogLevel, logger?: Logger): this {
|
|
540
|
+
this.config.logLevel = LogLevelSchema.parse(level);
|
|
541
|
+
if (logger) {
|
|
542
|
+
this.config.logger = LoggerSchema.parse(logger);
|
|
543
|
+
}
|
|
544
|
+
return this;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Configures agent caching for improved performance.
|
|
549
|
+
* Cache stores agent information to reduce lookup overhead.
|
|
550
|
+
* Automatically invalidates stale entries based on timeout.
|
|
551
|
+
*
|
|
552
|
+
* @param enabled - Enable/disable agent caching (default: true)
|
|
553
|
+
* @param timeout - Cache entry timeout in ms (default: 300000 / 5 minutes, range: 1000-3600000)
|
|
554
|
+
* @param maxSize - Maximum cache size (default: 100, range: 1-10000)
|
|
555
|
+
* @returns this builder for method chaining
|
|
556
|
+
* @throws {z.ZodError} If timeout or maxSize are out of valid range
|
|
557
|
+
*
|
|
558
|
+
* @example
|
|
559
|
+
* ```typescript
|
|
560
|
+
* // Enable with defaults
|
|
561
|
+
* builder.withCache(true)
|
|
562
|
+
*
|
|
563
|
+
* // Custom cache settings
|
|
564
|
+
* builder.withCache(true, 600000, 500) // 10 minutes, 500 entries
|
|
565
|
+
*
|
|
566
|
+
* // Disable caching
|
|
567
|
+
* builder.withCache(false)
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
withCache(enabled: boolean, timeout?: number, maxSize?: number): this {
|
|
571
|
+
this.config.enableCache = z.boolean().parse(enabled);
|
|
572
|
+
if (timeout !== undefined) {
|
|
573
|
+
this.config.cacheTimeout = z.number().min(1000).max(3600000).parse(timeout);
|
|
574
|
+
}
|
|
575
|
+
if (maxSize !== undefined) {
|
|
576
|
+
this.config.maxCacheSize = z.number().min(1).max(10000).parse(maxSize);
|
|
577
|
+
}
|
|
578
|
+
return this;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Configures message signature verification for security (SEC-2).
|
|
583
|
+
* Verifies Ethereum ECDSA signatures on incoming messages to prevent spoofing attacks.
|
|
584
|
+
* Disabled by default for backwards compatibility.
|
|
585
|
+
*
|
|
586
|
+
* @param options - Signature verification configuration
|
|
587
|
+
* @param options.enabled - Enable/disable signature verification (default: false)
|
|
588
|
+
* @param options.trustedAddresses - Whitelist of trusted agent addresses (empty = allow all)
|
|
589
|
+
* @param options.requireFor - Message types that require signatures (default: ['task_response', 'agent_selected'])
|
|
590
|
+
* @param options.strictMode - Reject all unsigned messages vs just critical ones (default: false)
|
|
591
|
+
* @returns this builder for method chaining
|
|
592
|
+
* @throws {z.ZodError} If options are invalid
|
|
593
|
+
*
|
|
594
|
+
* @example
|
|
595
|
+
* ```typescript
|
|
596
|
+
* // Enable with defaults (verify but allow unsigned non-critical messages)
|
|
597
|
+
* builder.withSignatureVerification({ enabled: true })
|
|
598
|
+
*
|
|
599
|
+
* // Enable with trusted address whitelist
|
|
600
|
+
* builder.withSignatureVerification({
|
|
601
|
+
* enabled: true,
|
|
602
|
+
* trustedAddresses: ['0xAgent1...', '0xAgent2...']
|
|
603
|
+
* })
|
|
604
|
+
*
|
|
605
|
+
* // Strict mode (reject all unsigned messages)
|
|
606
|
+
* builder.withSignatureVerification({
|
|
607
|
+
* enabled: true,
|
|
608
|
+
* strictMode: true,
|
|
609
|
+
* requireFor: ['task_response', 'agent_selected', 'message']
|
|
610
|
+
* })
|
|
611
|
+
* ```
|
|
612
|
+
*/
|
|
613
|
+
withSignatureVerification(options: {
|
|
614
|
+
enabled?: boolean;
|
|
615
|
+
trustedAddresses?: string[];
|
|
616
|
+
requireFor?: MessageType[];
|
|
617
|
+
strictMode?: boolean;
|
|
618
|
+
}): this {
|
|
619
|
+
const { enabled, trustedAddresses, requireFor, strictMode } = options;
|
|
620
|
+
|
|
621
|
+
if (enabled !== undefined) {
|
|
622
|
+
this.config.validateSignatures = z.boolean().parse(enabled);
|
|
623
|
+
}
|
|
624
|
+
if (trustedAddresses !== undefined) {
|
|
625
|
+
this.config.trustedAgentAddresses = z.array(z.string()).parse(trustedAddresses);
|
|
626
|
+
}
|
|
627
|
+
if (requireFor !== undefined) {
|
|
628
|
+
this.config.requireSignaturesFor = z.array(MessageTypeSchema).parse(requireFor);
|
|
629
|
+
}
|
|
630
|
+
if (strictMode !== undefined) {
|
|
631
|
+
this.config.strictSignatureValidation = z.boolean().parse(strictMode);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
return this;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Configures WebSocket reconnection retry strategy (REL-3).
|
|
639
|
+
* Allows full control over retry behavior: exponential, linear, or constant backoff.
|
|
640
|
+
* If not specified, uses exponential backoff with default parameters for backward compatibility.
|
|
641
|
+
*
|
|
642
|
+
* @param strategy - Partial retry strategy configuration (unspecified fields use defaults)
|
|
643
|
+
* @returns this builder for method chaining
|
|
644
|
+
* @throws {z.ZodError} If strategy parameters are invalid
|
|
645
|
+
*
|
|
646
|
+
* @example
|
|
647
|
+
* ```typescript
|
|
648
|
+
* // Exponential backoff with aggressive multiplier
|
|
649
|
+
* builder.withReconnectionStrategy({
|
|
650
|
+
* type: 'exponential',
|
|
651
|
+
* baseDelay: 3000,
|
|
652
|
+
* maxDelay: 120000,
|
|
653
|
+
* maxAttempts: 20,
|
|
654
|
+
* jitter: true,
|
|
655
|
+
* backoffMultiplier: 3
|
|
656
|
+
* })
|
|
657
|
+
*
|
|
658
|
+
* // Linear backoff for predictable delays
|
|
659
|
+
* builder.withReconnectionStrategy({
|
|
660
|
+
* type: 'linear',
|
|
661
|
+
* baseDelay: 5000,
|
|
662
|
+
* maxDelay: 60000,
|
|
663
|
+
* maxAttempts: 10,
|
|
664
|
+
* jitter: false
|
|
665
|
+
* })
|
|
666
|
+
*
|
|
667
|
+
* // Constant delay (useful for testing)
|
|
668
|
+
* builder.withReconnectionStrategy({
|
|
669
|
+
* type: 'constant',
|
|
670
|
+
* baseDelay: 10000,
|
|
671
|
+
* maxDelay: 10000,
|
|
672
|
+
* maxAttempts: 5,
|
|
673
|
+
* jitter: false
|
|
674
|
+
* })
|
|
675
|
+
* ```
|
|
676
|
+
*/
|
|
677
|
+
withReconnectionStrategy(strategy: Partial<RetryStrategy>): this {
|
|
678
|
+
// Merge with defaults to allow partial strategy specification
|
|
679
|
+
const fullStrategy: RetryStrategy = {
|
|
680
|
+
type: strategy.type || "exponential",
|
|
681
|
+
baseDelay: strategy.baseDelay !== undefined ? strategy.baseDelay : 5000,
|
|
682
|
+
maxDelay: strategy.maxDelay !== undefined ? strategy.maxDelay : 60000,
|
|
683
|
+
maxAttempts: strategy.maxAttempts !== undefined ? strategy.maxAttempts : 10,
|
|
684
|
+
jitter: strategy.jitter !== undefined ? strategy.jitter : true,
|
|
685
|
+
backoffMultiplier: strategy.backoffMultiplier
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
// Validate with schema
|
|
689
|
+
this.config.reconnectStrategy = RetryStrategySchema.parse(fullStrategy);
|
|
690
|
+
return this;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Configures webhook delivery retry strategy (REL-3).
|
|
695
|
+
* Allows full control over retry behavior: exponential, linear, or constant backoff.
|
|
696
|
+
* If not specified, uses exponential backoff with default parameters for backward compatibility.
|
|
697
|
+
*
|
|
698
|
+
* @param strategy - Partial retry strategy configuration (unspecified fields use defaults)
|
|
699
|
+
* @returns this builder for method chaining
|
|
700
|
+
* @throws {z.ZodError} If strategy parameters are invalid
|
|
701
|
+
*
|
|
702
|
+
* @example
|
|
703
|
+
* ```typescript
|
|
704
|
+
* // Exponential backoff without jitter
|
|
705
|
+
* builder.withWebhookRetryStrategy({
|
|
706
|
+
* type: 'exponential',
|
|
707
|
+
* baseDelay: 1000,
|
|
708
|
+
* maxDelay: 30000,
|
|
709
|
+
* maxAttempts: 5,
|
|
710
|
+
* jitter: false,
|
|
711
|
+
* backoffMultiplier: 2
|
|
712
|
+
* })
|
|
713
|
+
*
|
|
714
|
+
* // Linear backoff with jitter to spread load
|
|
715
|
+
* builder.withWebhookRetryStrategy({
|
|
716
|
+
* type: 'linear',
|
|
717
|
+
* baseDelay: 2000,
|
|
718
|
+
* maxDelay: 20000,
|
|
719
|
+
* maxAttempts: 3,
|
|
720
|
+
* jitter: true
|
|
721
|
+
* })
|
|
722
|
+
* ```
|
|
723
|
+
*/
|
|
724
|
+
withWebhookRetryStrategy(strategy: Partial<RetryStrategy>): this {
|
|
725
|
+
// Merge with defaults to allow partial strategy specification
|
|
726
|
+
const fullStrategy: RetryStrategy = {
|
|
727
|
+
type: strategy.type || "exponential",
|
|
728
|
+
baseDelay: strategy.baseDelay !== undefined ? strategy.baseDelay : 1000,
|
|
729
|
+
maxDelay: strategy.maxDelay !== undefined ? strategy.maxDelay : 30000,
|
|
730
|
+
maxAttempts: strategy.maxAttempts !== undefined ? strategy.maxAttempts : 3,
|
|
731
|
+
jitter: strategy.jitter !== undefined ? strategy.jitter : false,
|
|
732
|
+
backoffMultiplier: strategy.backoffMultiplier
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
// Validate with schema
|
|
736
|
+
this.config.webhookRetryStrategy = RetryStrategySchema.parse(fullStrategy);
|
|
737
|
+
return this;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Configures message deduplication to prevent duplicate processing (CB-4).
|
|
742
|
+
* Uses TTL-based cache to track recently processed message IDs.
|
|
743
|
+
* Automatically expires entries to prevent unbounded memory growth.
|
|
744
|
+
* Enabled by default with sensible limits for most use cases.
|
|
745
|
+
*
|
|
746
|
+
* @param enabled - Enable/disable message deduplication (default: true)
|
|
747
|
+
* @param ttl - How long to remember message IDs in milliseconds (default: 60000 / 1 minute, range: 1000-3600000)
|
|
748
|
+
* @param maxSize - Maximum cache size (default: 10000, range: 1-100000)
|
|
749
|
+
* @returns this builder for method chaining
|
|
750
|
+
* @throws {z.ZodError} If ttl or maxSize are out of valid range
|
|
751
|
+
*
|
|
752
|
+
* @example
|
|
753
|
+
* ```typescript
|
|
754
|
+
* // Enable with defaults (60s TTL, 10k cache)
|
|
755
|
+
* builder.withMessageDeduplication(true)
|
|
756
|
+
*
|
|
757
|
+
* // Custom settings for high-volume scenarios
|
|
758
|
+
* builder.withMessageDeduplication(true, 120000, 50000) // 2 minutes, 50k entries
|
|
759
|
+
*
|
|
760
|
+
* // Disable deduplication (not recommended for production)
|
|
761
|
+
* builder.withMessageDeduplication(false)
|
|
762
|
+
* ```
|
|
763
|
+
*/
|
|
764
|
+
withMessageDeduplication(enabled: boolean, ttl?: number, maxSize?: number): this {
|
|
765
|
+
this.config.enableMessageDeduplication = z.boolean().parse(enabled);
|
|
766
|
+
if (ttl !== undefined) {
|
|
767
|
+
this.config.messageDedupeTtl = z.number().min(1000).max(3600000).parse(ttl);
|
|
768
|
+
}
|
|
769
|
+
if (maxSize !== undefined) {
|
|
770
|
+
this.config.messageDedupMaxSize = z.number().min(1).max(100000).parse(maxSize);
|
|
771
|
+
}
|
|
772
|
+
return this;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Builds and validates the final SDK configuration.
|
|
777
|
+
* Performs comprehensive validation including custom refinements (e.g., webhook security).
|
|
778
|
+
* Must be called after setting all desired configuration options.
|
|
779
|
+
*
|
|
780
|
+
* @returns Validated SDK configuration ready to pass to TeneoSDK constructor
|
|
781
|
+
* @throws {Error} If configuration is invalid or fails validation
|
|
782
|
+
* @throws {z.ZodError} If required fields are missing or values are out of range
|
|
783
|
+
*
|
|
784
|
+
* @example
|
|
785
|
+
* ```typescript
|
|
786
|
+
* const config = new SDKConfigBuilder()
|
|
787
|
+
* .withWebSocketUrl('wss://teneo.example.com')
|
|
788
|
+
* .withAuthentication('0x...')
|
|
789
|
+
* .withAutoJoinRooms(['general'])
|
|
790
|
+
* .build(); // Validates and returns final config
|
|
791
|
+
*
|
|
792
|
+
* const sdk = new TeneoSDK(config);
|
|
793
|
+
* ```
|
|
794
|
+
*/
|
|
795
|
+
build(): SDKConfig {
|
|
796
|
+
// Validate and return complete config
|
|
797
|
+
return validateConfig(this.config);
|
|
798
|
+
}
|
|
799
|
+
}
|