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

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,4 +1,7 @@
1
1
  import type { ConnectionState, ConnectionOptions, Message, MessageHandler, ErrorHandler, CloseHandler } from '../types';
2
+ import type { Server as HttpServer } from 'http';
3
+ import type { Server as HttpsServer } from 'https';
4
+ import type { WebSocketServer } from 'ws';
2
5
  export interface ITransportAdapter {
3
6
  connect(url: string, options?: ConnectionOptions): Promise<void>;
4
7
  disconnect(): Promise<void>;
@@ -8,5 +11,7 @@ export interface ITransportAdapter {
8
11
  onClose(handler: CloseHandler): void;
9
12
  getState(): ConnectionState;
10
13
  isConnected(): boolean;
14
+ attach?(server: HttpServer | HttpsServer, path?: string): Promise<void>;
15
+ attachToWebSocketServer?(wss: WebSocketServer): Promise<void>;
11
16
  }
12
17
  //# sourceMappingURL=TransportAdapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TransportAdapter.d.ts","sourceRoot":"","sources":["../../src/abstractions/TransportAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,cAAc,EACd,YAAY,EACZ,YAAY,EACb,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,QAAQ,IAAI,eAAe,CAAC;IAC5B,WAAW,IAAI,OAAO,CAAC;CACxB"}
1
+ {"version":3,"file":"TransportAdapter.d.ts","sourceRoot":"","sources":["../../src/abstractions/TransportAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,cAAc,EACd,YAAY,EACZ,YAAY,EACb,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAE1C,MAAM,WAAW,iBAAiB;IAEhC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,QAAQ,IAAI,eAAe,CAAC;IAC5B,WAAW,IAAI,OAAO,CAAC;IAGvB,MAAM,CAAC,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,uBAAuB,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D"}
@@ -0,0 +1,40 @@
1
+ import type { ITransportAdapter } from '../../abstractions/TransportAdapter';
2
+ import type { ConnectionState, ConnectionOptions, Message, MessageHandler, ErrorHandler, CloseHandler } from '../../types';
3
+ import type { Server as HttpServer } from 'http';
4
+ import type { Server as HttpsServer } from 'https';
5
+ import { WebSocketServer, WebSocket } from 'ws';
6
+ interface ClientConnection {
7
+ id: string;
8
+ ws: WebSocket;
9
+ userId?: string;
10
+ }
11
+ export declare class WebSocketTransportAdapter implements ITransportAdapter {
12
+ private state;
13
+ private messageHandlers;
14
+ private errorHandlers;
15
+ private closeHandlers;
16
+ private wss?;
17
+ private serverUrl?;
18
+ private attachedServer?;
19
+ private attachedWss?;
20
+ private webSocketPath?;
21
+ private clients;
22
+ private mode;
23
+ connect(url: string, _options?: ConnectionOptions): Promise<void>;
24
+ attach(server: HttpServer | HttpsServer, path?: string): Promise<void>;
25
+ attachToWebSocketServer(wss: WebSocketServer): Promise<void>;
26
+ private handleConnection;
27
+ disconnect(): Promise<void>;
28
+ send(message: Message): Promise<void>;
29
+ onMessage(handler: MessageHandler): void;
30
+ onError(handler: ErrorHandler): void;
31
+ onClose(handler: CloseHandler): void;
32
+ getState(): ConnectionState;
33
+ isConnected(): boolean;
34
+ getConnectedClients(): ClientConnection[];
35
+ getClientCount(): number;
36
+ getMode(): 'standalone' | 'integration';
37
+ private generateId;
38
+ }
39
+ export {};
40
+ //# sourceMappingURL=WebSocketTransportAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSocketTransportAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/websocket/WebSocketTransportAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,cAAc,EACd,YAAY,EACZ,YAAY,EACb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,OAAO,CAAC;AACnD,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;CACjB;AAED,qBAAa,yBAA0B,YAAW,iBAAiB;IACjE,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;IAGpD,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;IA2BlE,OAAO,CAAC,gBAAgB;IAqClB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B3B,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,cAAc,IAAI,MAAM;IAIxB,OAAO,IAAI,YAAY,GAAG,aAAa;IAIvC,OAAO,CAAC,UAAU;CAGnB"}
@@ -0,0 +1,204 @@
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
+ }
@@ -0,0 +1,2 @@
1
+ export { WebSocketTransportAdapter } from './WebSocketTransportAdapter';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/websocket/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1 @@
1
+ export { WebSocketTransportAdapter } from './WebSocketTransportAdapter';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { EventType, BaseEvent, FileAction, FileChangeEvent, GitStatusEvent, CommitEvent, BranchChangeEvent, CursorPositionEvent, Event, TokenPayload, AuthResult, CredentialType, OAuthProvider, Credentials, JWTConfig, OAuthConfig, AuthProvider, RoomPermission, Room, RoomUser, UserStatus, RoomState, RoomConfig, LockType, LockPriority, Lock, LockRequest, LockQueueItem, LockState, ConnectionState, ConnectionOptions, Message, MessageHandler, ErrorHandler, CloseHandler } from './types';
2
2
  export { ITransportAdapter, IStorageAdapter, StorageOperation, IAuthAdapter, TypedEventEmitter, EventListener, UnsubscribeFn, RoomManager, LockManager, DefaultRoomManager, DefaultLockManager } from './abstractions';
3
3
  export { MockTransportAdapter, MockStorageAdapter, MockAuthAdapter } from './adapters/mock';
4
+ export { WebSocketTransportAdapter } from './adapters/websocket';
4
5
  export { BaseClient, ClientBuilder, type ClientConfig, type ClientEvents } from './client';
5
6
  export { BaseServer, ServerBuilder, type ServerConfig, type ServerEvents, type ConnectedClient } from './server';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,SAAS,EACT,SAAS,EACT,UAAU,EACV,eAAe,EACf,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,EAEL,YAAY,EACZ,UAAU,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,SAAS,EACT,WAAW,EACX,YAAY,EAEZ,cAAc,EACd,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,SAAS,EACT,UAAU,EAEV,QAAQ,EACR,YAAY,EACZ,IAAI,EACJ,WAAW,EACX,aAAa,EACb,SAAS,EAET,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,cAAc,EACd,YAAY,EACZ,YAAY,EACb,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,UAAU,EACV,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,UAAU,EACV,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,SAAS,EACT,SAAS,EACT,UAAU,EACV,eAAe,EACf,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,EAEL,YAAY,EACZ,UAAU,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,SAAS,EACT,WAAW,EACX,YAAY,EAEZ,cAAc,EACd,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,SAAS,EACT,UAAU,EAEV,QAAQ,EACR,YAAY,EACZ,IAAI,EACJ,WAAW,EACX,aAAa,EACb,SAAS,EAET,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,cAAc,EACd,YAAY,EACZ,YAAY,EACb,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,yBAAyB,EAC1B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,UAAU,EACV,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,UAAU,EACV,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  // Abstractions
2
2
  export { TypedEventEmitter, RoomManager, LockManager, DefaultRoomManager, DefaultLockManager } from './abstractions';
3
- // Mock Adapters (for testing)
3
+ // Adapters
4
4
  export { MockTransportAdapter, MockStorageAdapter, MockAuthAdapter } from './adapters/mock';
5
+ export { WebSocketTransportAdapter } from './adapters/websocket';
5
6
  // Client
6
7
  export { BaseClient, ClientBuilder } from './client';
7
8
  // Server