@principal-ai/control-tower-core 0.1.9 → 0.1.11

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.
@@ -0,0 +1,78 @@
1
+ import type { ITransportAdapter } from '../../abstractions/TransportAdapter.js';
2
+ import type { ConnectionState, ConnectionOptions, Message, MessageHandler, ErrorHandler, CloseHandler } from '../../types/index.js';
3
+ import { WebSocket } from 'ws';
4
+ export interface WebSocketClientConfig {
5
+ /**
6
+ * Additional headers to send with the WebSocket connection request
7
+ */
8
+ headers?: Record<string, string>;
9
+ /**
10
+ * Connection timeout in milliseconds
11
+ * @default 30000
12
+ */
13
+ connectionTimeout?: number;
14
+ /**
15
+ * Enable automatic ping/pong for connection health checks
16
+ * @default true
17
+ */
18
+ enableHeartbeat?: boolean;
19
+ /**
20
+ * Heartbeat interval in milliseconds
21
+ * @default 30000
22
+ */
23
+ heartbeatInterval?: number;
24
+ }
25
+ /**
26
+ * WebSocket client transport adapter for connecting to remote WebSocket servers.
27
+ *
28
+ * This adapter creates an outgoing WebSocket connection and is intended for use
29
+ * with BaseClient. For server-side operations (accepting incoming connections),
30
+ * use WebSocketServerTransportAdapter instead.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const transport = new WebSocketClientTransportAdapter();
35
+ * const client = new ClientBuilder()
36
+ * .withTransport(transport)
37
+ * .build();
38
+ *
39
+ * await client.connect('ws://localhost:3000');
40
+ * ```
41
+ */
42
+ export declare class WebSocketClientTransportAdapter implements ITransportAdapter {
43
+ private ws?;
44
+ private state;
45
+ private messageHandlers;
46
+ private errorHandlers;
47
+ private closeHandlers;
48
+ private config;
49
+ private heartbeatTimer?;
50
+ private connectionTimer?;
51
+ constructor(config?: WebSocketClientConfig);
52
+ connect(url: string, options?: ConnectionOptions): Promise<void>;
53
+ disconnect(): Promise<void>;
54
+ send(message: Message): Promise<void>;
55
+ onMessage(handler: MessageHandler): void;
56
+ onError(handler: ErrorHandler): void;
57
+ onClose(handler: CloseHandler): void;
58
+ getState(): ConnectionState;
59
+ isConnected(): boolean;
60
+ /**
61
+ * Get the underlying WebSocket instance
62
+ * Useful for advanced use cases
63
+ */
64
+ getWebSocket(): WebSocket | undefined;
65
+ /**
66
+ * Set custom headers for the WebSocket connection
67
+ * Must be called before connect()
68
+ */
69
+ setHeaders(headers: Record<string, string>): void;
70
+ /**
71
+ * Add an authorization header for authentication
72
+ * Must be called before connect()
73
+ */
74
+ setAuthToken(token: string): void;
75
+ private startHeartbeat;
76
+ private stopHeartbeat;
77
+ }
78
+ //# sourceMappingURL=WebSocketClientTransportAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSocketClientTransportAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/websocket/WebSocketClientTransportAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,cAAc,EACd,YAAY,EACZ,YAAY,EACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAE/B,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,+BAAgC,YAAW,iBAAiB;IACvE,OAAO,CAAC,EAAE,CAAC,CAAY;IACvB,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,eAAe,CAAkC;IACzD,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAiB;gBAE7B,MAAM,CAAC,EAAE,qBAAqB;IASpC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2GhE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsC3B,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB3C,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAIxC,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAIpC,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAIpC,QAAQ,IAAI,eAAe;IAI3B,WAAW,IAAI,OAAO;IAItB;;;OAGG;IACH,YAAY,IAAI,SAAS,GAAG,SAAS;IAIrC;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAOjD;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAMjC,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,aAAa;CAMtB"}
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebSocketClientTransportAdapter = void 0;
4
+ const ws_1 = require("ws");
5
+ /**
6
+ * WebSocket client transport adapter for connecting to remote WebSocket servers.
7
+ *
8
+ * This adapter creates an outgoing WebSocket connection and is intended for use
9
+ * with BaseClient. For server-side operations (accepting incoming connections),
10
+ * use WebSocketServerTransportAdapter instead.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const transport = new WebSocketClientTransportAdapter();
15
+ * const client = new ClientBuilder()
16
+ * .withTransport(transport)
17
+ * .build();
18
+ *
19
+ * await client.connect('ws://localhost:3000');
20
+ * ```
21
+ */
22
+ class WebSocketClientTransportAdapter {
23
+ constructor(config) {
24
+ this.state = 'disconnected';
25
+ this.messageHandlers = new Set();
26
+ this.errorHandlers = new Set();
27
+ this.closeHandlers = new Set();
28
+ this.config = {
29
+ headers: config?.headers ?? {},
30
+ connectionTimeout: config?.connectionTimeout ?? 30000,
31
+ enableHeartbeat: config?.enableHeartbeat ?? true,
32
+ heartbeatInterval: config?.heartbeatInterval ?? 30000
33
+ };
34
+ }
35
+ async connect(url, options) {
36
+ if (this.state === 'connected' || this.state === 'connecting') {
37
+ throw new Error('Already connected or connecting');
38
+ }
39
+ this.state = 'connecting';
40
+ return new Promise((resolve, reject) => {
41
+ try {
42
+ // Create WebSocket client connection
43
+ const wsOptions = {};
44
+ // Add custom headers if provided
45
+ if (Object.keys(this.config.headers).length > 0) {
46
+ wsOptions.headers = this.config.headers;
47
+ }
48
+ this.ws = new ws_1.WebSocket(url, wsOptions);
49
+ // Set up connection timeout
50
+ this.connectionTimer = setTimeout(() => {
51
+ if (this.state === 'connecting') {
52
+ this.ws?.terminate();
53
+ this.state = 'disconnected';
54
+ const error = new Error('Connection timeout');
55
+ this.errorHandlers.forEach(handler => handler(error));
56
+ reject(error);
57
+ }
58
+ }, this.config.connectionTimeout);
59
+ // Handle connection open
60
+ this.ws.on('open', () => {
61
+ if (this.connectionTimer) {
62
+ clearTimeout(this.connectionTimer);
63
+ this.connectionTimer = undefined;
64
+ }
65
+ this.state = 'connected';
66
+ // Start heartbeat if enabled
67
+ if (this.config.enableHeartbeat) {
68
+ this.startHeartbeat();
69
+ }
70
+ resolve();
71
+ });
72
+ // Handle incoming messages
73
+ this.ws.on('message', (data) => {
74
+ try {
75
+ const message = JSON.parse(data.toString());
76
+ this.messageHandlers.forEach(handler => handler(message));
77
+ }
78
+ catch (error) {
79
+ this.errorHandlers.forEach(handler => handler(error));
80
+ }
81
+ });
82
+ // Handle errors
83
+ this.ws.on('error', (error) => {
84
+ if (this.connectionTimer) {
85
+ clearTimeout(this.connectionTimer);
86
+ this.connectionTimer = undefined;
87
+ }
88
+ this.errorHandlers.forEach(handler => handler(error));
89
+ // If error occurs during connection, reject the promise
90
+ if (this.state === 'connecting') {
91
+ this.state = 'disconnected';
92
+ reject(error);
93
+ }
94
+ });
95
+ // Handle connection close
96
+ this.ws.on('close', (code, reason) => {
97
+ if (this.connectionTimer) {
98
+ clearTimeout(this.connectionTimer);
99
+ this.connectionTimer = undefined;
100
+ }
101
+ this.stopHeartbeat();
102
+ const wasConnected = this.state === 'connected';
103
+ this.state = 'disconnected';
104
+ if (wasConnected) {
105
+ this.closeHandlers.forEach(handler => handler(code, reason.toString()));
106
+ }
107
+ });
108
+ // Handle pong responses for heartbeat
109
+ if (this.config.enableHeartbeat) {
110
+ this.ws.on('pong', () => {
111
+ // Connection is alive
112
+ });
113
+ }
114
+ }
115
+ catch (error) {
116
+ if (this.connectionTimer) {
117
+ clearTimeout(this.connectionTimer);
118
+ this.connectionTimer = undefined;
119
+ }
120
+ this.state = 'disconnected';
121
+ reject(error);
122
+ }
123
+ });
124
+ }
125
+ async disconnect() {
126
+ if (this.state === 'disconnected') {
127
+ return;
128
+ }
129
+ this.state = 'disconnecting';
130
+ this.stopHeartbeat();
131
+ if (this.connectionTimer) {
132
+ clearTimeout(this.connectionTimer);
133
+ this.connectionTimer = undefined;
134
+ }
135
+ if (this.ws) {
136
+ // Close with normal closure code
137
+ this.ws.close(1000, 'Client disconnecting');
138
+ // Wait for close event or timeout
139
+ await new Promise((resolve) => {
140
+ const timeout = setTimeout(() => {
141
+ this.ws?.terminate();
142
+ resolve();
143
+ }, 5000);
144
+ const onClose = () => {
145
+ clearTimeout(timeout);
146
+ resolve();
147
+ };
148
+ this.ws?.once('close', onClose);
149
+ });
150
+ this.ws = undefined;
151
+ }
152
+ this.state = 'disconnected';
153
+ }
154
+ async send(message) {
155
+ if (this.state !== 'connected') {
156
+ throw new Error('Not connected');
157
+ }
158
+ if (!this.ws || this.ws.readyState !== ws_1.WebSocket.OPEN) {
159
+ throw new Error('WebSocket connection is not open');
160
+ }
161
+ try {
162
+ const data = JSON.stringify(message);
163
+ this.ws.send(data);
164
+ }
165
+ catch (error) {
166
+ this.errorHandlers.forEach(handler => handler(error));
167
+ throw error;
168
+ }
169
+ }
170
+ onMessage(handler) {
171
+ this.messageHandlers.add(handler);
172
+ }
173
+ onError(handler) {
174
+ this.errorHandlers.add(handler);
175
+ }
176
+ onClose(handler) {
177
+ this.closeHandlers.add(handler);
178
+ }
179
+ getState() {
180
+ return this.state;
181
+ }
182
+ isConnected() {
183
+ return this.state === 'connected' && this.ws?.readyState === ws_1.WebSocket.OPEN;
184
+ }
185
+ /**
186
+ * Get the underlying WebSocket instance
187
+ * Useful for advanced use cases
188
+ */
189
+ getWebSocket() {
190
+ return this.ws;
191
+ }
192
+ /**
193
+ * Set custom headers for the WebSocket connection
194
+ * Must be called before connect()
195
+ */
196
+ setHeaders(headers) {
197
+ if (this.state !== 'disconnected') {
198
+ throw new Error('Cannot set headers while connected');
199
+ }
200
+ this.config.headers = { ...this.config.headers, ...headers };
201
+ }
202
+ /**
203
+ * Add an authorization header for authentication
204
+ * Must be called before connect()
205
+ */
206
+ setAuthToken(token) {
207
+ this.setHeaders({ Authorization: `Bearer ${token}` });
208
+ }
209
+ // Private methods
210
+ startHeartbeat() {
211
+ if (this.heartbeatTimer) {
212
+ clearInterval(this.heartbeatTimer);
213
+ }
214
+ this.heartbeatTimer = setInterval(() => {
215
+ if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN) {
216
+ this.ws.ping();
217
+ }
218
+ }, this.config.heartbeatInterval);
219
+ }
220
+ stopHeartbeat() {
221
+ if (this.heartbeatTimer) {
222
+ clearInterval(this.heartbeatTimer);
223
+ this.heartbeatTimer = undefined;
224
+ }
225
+ }
226
+ }
227
+ exports.WebSocketClientTransportAdapter = WebSocketClientTransportAdapter;
@@ -0,0 +1,60 @@
1
+ import type { ITransportAdapter } from '../../abstractions/TransportAdapter.js';
2
+ import type { IAuthAdapter } from '../../abstractions/AuthAdapter.js';
3
+ import type { ConnectionState, ConnectionOptions, Message, MessageHandler, ErrorHandler, CloseHandler } from '../../types/index.js';
4
+ import type { Server as HttpServer } from 'http';
5
+ import type { Server as HttpsServer } from 'https';
6
+ import { WebSocketServer, WebSocket } from 'ws';
7
+ interface ClientConnection {
8
+ id: string;
9
+ ws: WebSocket;
10
+ userId?: string;
11
+ authenticated: boolean;
12
+ metadata?: Record<string, unknown>;
13
+ authTimeout?: NodeJS.Timeout;
14
+ connectedAt: number;
15
+ }
16
+ export interface WebSocketServerTransportConfig {
17
+ authTimeout?: number;
18
+ closeOnAuthFailure?: boolean;
19
+ requireAuth?: boolean;
20
+ }
21
+ export declare class WebSocketServerTransportAdapter implements ITransportAdapter {
22
+ private state;
23
+ private messageHandlers;
24
+ private errorHandlers;
25
+ private closeHandlers;
26
+ private wss?;
27
+ private serverUrl?;
28
+ private attachedServer?;
29
+ private attachedWss?;
30
+ private webSocketPath?;
31
+ private clients;
32
+ private mode;
33
+ private authAdapter?;
34
+ private config;
35
+ constructor(config?: WebSocketServerTransportConfig);
36
+ setAuthAdapter(auth: IAuthAdapter): void;
37
+ connect(url: string, _options?: ConnectionOptions): Promise<void>;
38
+ attach(server: HttpServer | HttpsServer, path?: string): Promise<void>;
39
+ attachToWebSocketServer(wss: WebSocketServer): Promise<void>;
40
+ private handleConnection;
41
+ private handleClientMessage;
42
+ private handleAuthMessage;
43
+ private extractBearerToken;
44
+ private sendToClient;
45
+ disconnect(): Promise<void>;
46
+ send(message: Message): Promise<void>;
47
+ onMessage(handler: MessageHandler): void;
48
+ onError(handler: ErrorHandler): void;
49
+ onClose(handler: CloseHandler): void;
50
+ getState(): ConnectionState;
51
+ isConnected(): boolean;
52
+ getConnectedClients(): ClientConnection[];
53
+ getAuthenticatedClients(): ClientConnection[];
54
+ getClientCount(): number;
55
+ getMode(): 'standalone' | 'integration';
56
+ isAuthRequired(): boolean;
57
+ private generateId;
58
+ }
59
+ export {};
60
+ //# sourceMappingURL=WebSocketServerTransportAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSocketServerTransportAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/websocket/WebSocketServerTransportAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,cAAc,EACd,YAAY,EACZ,YAAY,EAGb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,OAAO,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAEhD,UAAU,gBAAgB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,8BAA8B;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,qBAAa,+BAAgC,YAAW,iBAAiB;IACvE,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,eAAe,CAAkC;IACzD,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,aAAa,CAAgC;IAGrD,OAAO,CAAC,GAAG,CAAC,CAAkB;IAC9B,OAAO,CAAC,SAAS,CAAC,CAAS;IAG3B,OAAO,CAAC,cAAc,CAAC,CAA2B;IAClD,OAAO,CAAC,WAAW,CAAC,CAAkB;IACtC,OAAO,CAAC,aAAa,CAAC,CAAS;IAG/B,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,IAAI,CAA8C;IAG1D,OAAO,CAAC,WAAW,CAAC,CAAe;IACnC,OAAO,CAAC,MAAM,CAAiC;gBAEnC,MAAM,CAAC,EAAE,8BAA8B;IAUnD,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAclC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsCjE,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,EAAE,IAAI,GAAE,MAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAiC7E,uBAAuB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;YA2BpD,gBAAgB;YA6EhB,mBAAmB;YAuCnB,iBAAiB;IA4E/B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,YAAY;IAMd,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC3B,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B3C,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAIxC,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAIpC,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAIpC,QAAQ,IAAI,eAAe;IAI3B,WAAW,IAAI,OAAO;IAKtB,mBAAmB,IAAI,gBAAgB,EAAE;IAIzC,uBAAuB,IAAI,gBAAgB,EAAE;IAI7C,cAAc,IAAI,MAAM;IAIxB,OAAO,IAAI,YAAY,GAAG,aAAa;IAIvC,cAAc,IAAI,OAAO;IAIzB,OAAO,CAAC,UAAU;CAGnB"}