@principal-ai/control-tower-core 0.1.2 → 0.1.4

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.
@@ -1,204 +0,0 @@
1
- import { WebSocketServer, WebSocket } from 'ws';
2
- export class WebSocketTransportAdapter {
3
- constructor() {
4
- this.state = 'disconnected';
5
- this.messageHandlers = new Set();
6
- this.errorHandlers = new Set();
7
- this.closeHandlers = new Set();
8
- // Client management
9
- this.clients = new Map();
10
- this.mode = 'standalone';
11
- }
12
- // Standalone mode implementation
13
- async connect(url, _options) {
14
- if (this.state === 'connected' || this.state === 'connecting') {
15
- throw new Error('Already connected or connecting');
16
- }
17
- this.state = 'connecting';
18
- this.serverUrl = url;
19
- this.mode = 'standalone';
20
- try {
21
- // Extract port from URL
22
- const urlObj = new URL(url);
23
- const port = parseInt(urlObj.port) || (urlObj.protocol === 'wss:' ? 443 : 80);
24
- // Create WebSocket server
25
- this.wss = new WebSocketServer({ port });
26
- this.wss.on('connection', (ws, req) => {
27
- this.handleConnection(ws, req);
28
- });
29
- this.wss.on('error', (error) => {
30
- this.errorHandlers.forEach(handler => handler(error));
31
- });
32
- this.wss.on('close', () => {
33
- this.state = 'disconnected';
34
- this.closeHandlers.forEach(handler => handler(1000, 'Server closed'));
35
- });
36
- this.state = 'connected';
37
- }
38
- catch (error) {
39
- this.state = 'disconnected';
40
- throw error;
41
- }
42
- }
43
- // Integration mode implementation
44
- async attach(server, path = '/ws') {
45
- if (this.state === 'connected' || this.state === 'connecting') {
46
- throw new Error('Already connected or connecting');
47
- }
48
- this.state = 'connecting';
49
- this.attachedServer = server;
50
- this.webSocketPath = path;
51
- this.mode = 'integration';
52
- try {
53
- // Create WebSocket server attached to the existing HTTP server
54
- this.wss = new WebSocketServer({
55
- server,
56
- path,
57
- perMessageDeflate: false
58
- });
59
- this.wss.on('connection', (ws, req) => {
60
- this.handleConnection(ws, req);
61
- });
62
- this.wss.on('error', (error) => {
63
- this.errorHandlers.forEach(handler => handler(error));
64
- });
65
- this.state = 'connected';
66
- }
67
- catch (error) {
68
- this.state = 'disconnected';
69
- throw error;
70
- }
71
- }
72
- async attachToWebSocketServer(wss) {
73
- if (this.state === 'connected' || this.state === 'connecting') {
74
- throw new Error('Already connected or connecting');
75
- }
76
- this.state = 'connecting';
77
- this.attachedWss = wss;
78
- this.mode = 'integration';
79
- try {
80
- this.wss = wss;
81
- this.wss.on('connection', (ws, req) => {
82
- this.handleConnection(ws, req);
83
- });
84
- this.wss.on('error', (error) => {
85
- this.errorHandlers.forEach(handler => handler(error));
86
- });
87
- this.state = 'connected';
88
- }
89
- catch (error) {
90
- this.state = 'disconnected';
91
- throw error;
92
- }
93
- }
94
- handleConnection(ws, req) {
95
- const clientId = this.generateId();
96
- const client = {
97
- id: clientId,
98
- ws,
99
- userId: typeof req.headers['x-user-id'] === 'string' ? req.headers['x-user-id'] : undefined
100
- };
101
- this.clients.set(clientId, client);
102
- ws.on('message', (data) => {
103
- try {
104
- const message = JSON.parse(data.toString());
105
- // Add clientId to message payload for routing
106
- const enrichedMessage = {
107
- ...message,
108
- payload: {
109
- ...message.payload,
110
- clientId
111
- }
112
- };
113
- this.messageHandlers.forEach(handler => handler(enrichedMessage));
114
- }
115
- catch (error) {
116
- this.errorHandlers.forEach(handler => handler(error));
117
- }
118
- });
119
- ws.on('error', (error) => {
120
- this.errorHandlers.forEach(handler => handler(error));
121
- });
122
- ws.on('close', (_code, _reason) => {
123
- this.clients.delete(clientId);
124
- // Don't emit close for individual clients, only for server shutdown
125
- });
126
- }
127
- async disconnect() {
128
- if (this.state === 'disconnected') {
129
- return;
130
- }
131
- this.state = 'disconnecting';
132
- // Close all client connections
133
- for (const [_clientId, client] of this.clients) {
134
- try {
135
- client.ws.close(1000, 'Server shutting down');
136
- }
137
- catch {
138
- // Ignore errors when closing individual connections
139
- }
140
- }
141
- this.clients.clear();
142
- // Close the WebSocket server (only if we created it)
143
- if (this.wss && this.mode === 'standalone') {
144
- await new Promise((resolve) => {
145
- this.wss.close(() => resolve());
146
- });
147
- }
148
- this.state = 'disconnected';
149
- this.wss = undefined;
150
- this.attachedServer = undefined;
151
- this.attachedWss = undefined;
152
- }
153
- async send(message) {
154
- if (this.state !== 'connected') {
155
- throw new Error('Not connected');
156
- }
157
- // Extract clientId from message payload for routing
158
- const payload = message.payload;
159
- const { clientId, ...clientMessage } = payload;
160
- if (!clientId) {
161
- throw new Error('Message must contain clientId in payload for routing');
162
- }
163
- const client = this.clients.get(clientId);
164
- if (!client) {
165
- throw new Error(`Client ${clientId} not found`);
166
- }
167
- if (client.ws.readyState !== WebSocket.OPEN) {
168
- throw new Error(`Client ${clientId} connection is not open`);
169
- }
170
- const messageToSend = {
171
- ...message,
172
- payload: clientMessage
173
- };
174
- client.ws.send(JSON.stringify(messageToSend));
175
- }
176
- onMessage(handler) {
177
- this.messageHandlers.add(handler);
178
- }
179
- onError(handler) {
180
- this.errorHandlers.add(handler);
181
- }
182
- onClose(handler) {
183
- this.closeHandlers.add(handler);
184
- }
185
- getState() {
186
- return this.state;
187
- }
188
- isConnected() {
189
- return this.state === 'connected';
190
- }
191
- // Utility methods
192
- getConnectedClients() {
193
- return Array.from(this.clients.values());
194
- }
195
- getClientCount() {
196
- return this.clients.size;
197
- }
198
- getMode() {
199
- return this.mode;
200
- }
201
- generateId() {
202
- return Math.random().toString(36).substring(2) + Date.now().toString(36);
203
- }
204
- }
@@ -1,2 +0,0 @@
1
- export { WebSocketTransportAdapter } from './WebSocketTransportAdapter';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/websocket/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -1 +0,0 @@
1
- export { WebSocketTransportAdapter } from './WebSocketTransportAdapter';