@principal-ai/control-tower-core 0.2.0 → 0.2.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.
Files changed (42) hide show
  1. package/dist/abstractions/ConnectedRoomManager.d.ts +22 -0
  2. package/dist/abstractions/ConnectedRoomManager.d.ts.map +1 -0
  3. package/dist/abstractions/ConnectedRoomManager.js +56 -0
  4. package/dist/abstractions/index.d.ts +1 -0
  5. package/dist/abstractions/index.d.ts.map +1 -1
  6. package/dist/abstractions/index.js +3 -1
  7. package/dist/generated/client-connection-auth.types.d.ts +312 -0
  8. package/dist/generated/client-connection-auth.types.d.ts.map +1 -0
  9. package/dist/generated/client-connection-auth.types.js +11 -0
  10. package/dist/generated/control-tower-execution.types.d.ts +445 -0
  11. package/dist/generated/control-tower-execution.types.d.ts.map +1 -0
  12. package/dist/generated/control-tower-execution.types.js +11 -0
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -1
  16. package/dist/index.js.map +8 -7
  17. package/dist/index.mjs +180 -99
  18. package/dist/index.mjs.map +8 -7
  19. package/dist/server/BaseServer.d.ts +30 -2
  20. package/dist/server/BaseServer.d.ts.map +1 -1
  21. package/dist/server/BaseServer.js +77 -8
  22. package/dist/server/ServerBuilder.d.ts.map +1 -1
  23. package/dist/server/ServerBuilder.js +11 -0
  24. package/dist/telemetry/EventValidationIntegration.d.ts +135 -0
  25. package/dist/telemetry/EventValidationIntegration.d.ts.map +1 -0
  26. package/dist/telemetry/EventValidationIntegration.js +253 -0
  27. package/dist/telemetry/EventValidationIntegration.test.d.ts +7 -0
  28. package/dist/telemetry/EventValidationIntegration.test.d.ts.map +1 -0
  29. package/dist/telemetry/EventValidationIntegration.test.js +322 -0
  30. package/dist/telemetry/TelemetryCapture.d.ts +268 -0
  31. package/dist/telemetry/TelemetryCapture.d.ts.map +1 -0
  32. package/dist/telemetry/TelemetryCapture.js +263 -0
  33. package/dist/telemetry/TelemetryCapture.test.d.ts +7 -0
  34. package/dist/telemetry/TelemetryCapture.test.d.ts.map +1 -0
  35. package/dist/telemetry/TelemetryCapture.test.js +396 -0
  36. package/dist/telemetry-example.d.ts +33 -0
  37. package/dist/telemetry-example.d.ts.map +1 -0
  38. package/dist/telemetry-example.js +124 -0
  39. package/package.json +1 -1
  40. package/dist/adapters/websocket/WebSocketTransportAdapter.d.ts +0 -60
  41. package/dist/adapters/websocket/WebSocketTransportAdapter.d.ts.map +0 -1
  42. package/dist/adapters/websocket/WebSocketTransportAdapter.js +0 -386
@@ -26,6 +26,12 @@ export interface ServerConfig {
26
26
  export interface ConnectedClient {
27
27
  id: string;
28
28
  userId: string;
29
+ /**
30
+ * Device ID for presence tracking.
31
+ * By default equals clientId, but can be overridden via setClientDeviceId()
32
+ * to support multi-window scenarios where multiple clients share a device.
33
+ */
34
+ deviceId: string;
29
35
  /**
30
36
  * Set of room IDs the client is currently in
31
37
  *
@@ -204,14 +210,36 @@ export declare class BaseServer extends TypedEventEmitter<ServerEvents> {
204
210
  * @returns Array of client IDs
205
211
  */
206
212
  getClientIdsForUser(userId: string): string[];
213
+ /**
214
+ * Get all client IDs for a user in a specific room
215
+ *
216
+ * @param roomId - Room identifier
217
+ * @param userId - User identifier
218
+ * @returns Array of client IDs
219
+ */
220
+ getClientIdsInRoom(roomId: string, userId: string): string[];
207
221
  /**
208
222
  * Get device ID from client ID
209
223
  *
210
- * In Control Tower architecture, clientId === deviceId
224
+ * By default, deviceId === clientId. Use setClientDeviceId() to override
225
+ * for multi-window scenarios where multiple clients share a device.
211
226
  *
212
227
  * @param clientId - Client identifier
213
- * @returns Device ID (same as clientId)
228
+ * @returns Device ID for this client
214
229
  */
215
230
  getDeviceIdFromClientId(clientId: string): string;
231
+ /**
232
+ * Set the device ID for a client
233
+ *
234
+ * Use this to associate a custom deviceId with a client, e.g., when
235
+ * the deviceId comes from a JWT token and differs from the WebSocket clientId.
236
+ * This is useful for multi-window scenarios where multiple WebSocket clients
237
+ * share the same device/user session.
238
+ *
239
+ * @param clientId - Client identifier
240
+ * @param deviceId - Device identifier to associate with this client
241
+ * @returns true if the client was found and updated, false otherwise
242
+ */
243
+ setClientDeviceId(clientId: string, deviceId: string): boolean;
216
244
  }
217
245
  //# sourceMappingURL=BaseServer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BaseServer.d.ts","sourceRoot":"","sources":["../../src/server/BaseServer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BaseServer.d.ts","sourceRoot":"","sources":["../../src/server/BaseServer.ts"],"names":[],"mappings":"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"}
@@ -254,6 +254,7 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
254
254
  const client = {
255
255
  id: payload.clientId,
256
256
  userId: payload.userId || "",
257
+ deviceId: payload.clientId, // Default to clientId, can be overridden via setClientDeviceId()
257
258
  roomIds,
258
259
  get roomId() {
259
260
  return roomIds.size > 0 ? roomIds.values().next().value || null : null;
@@ -340,6 +341,7 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
340
341
  const client = {
341
342
  id: clientId,
342
343
  userId: "",
344
+ deviceId: clientId, // Default to clientId, can be overridden via setClientDeviceId()
343
345
  roomIds,
344
346
  get roomId() {
345
347
  return roomIds.size > 0 ? roomIds.values().next().value || null : null;
@@ -371,10 +373,10 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
371
373
  // Unregister from presence manager
372
374
  if (this.presenceManager?.isEnabled() && client.userId) {
373
375
  try {
374
- const presence = await this.presenceManager.disconnectDevice(client.userId, clientId);
376
+ const presence = await this.presenceManager.disconnectDevice(client.userId, client.deviceId);
375
377
  await this.emit("presence_device_disconnected", {
376
378
  userId: client.userId,
377
- deviceId: clientId,
379
+ deviceId: client.deviceId,
378
380
  timestamp: Date.now(),
379
381
  });
380
382
  // Broadcast presence update if user went offline or into grace period
@@ -385,7 +387,7 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
385
387
  type: "presence:user_offline",
386
388
  payload: {
387
389
  userId: client.userId,
388
- deviceId: clientId,
390
+ deviceId: client.deviceId,
389
391
  status: presence?.status || "offline",
390
392
  gracePeriod: presenceConfig.gracePeriod,
391
393
  },
@@ -399,8 +401,23 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
399
401
  });
400
402
  }
401
403
  }
402
- // Leave all rooms the client is in
404
+ // Leave all rooms the client is in and broadcast to other room members
403
405
  for (const roomId of client.roomIds) {
406
+ // Broadcast member_left to other room members
407
+ try {
408
+ await this.experimental.broadcastWhere((c) => c.roomIds.has(roomId) && c.id !== clientId, {
409
+ type: "member_left",
410
+ payload: {
411
+ roomId,
412
+ userId: client.userId,
413
+ clientId,
414
+ timestamp: Date.now(),
415
+ },
416
+ });
417
+ }
418
+ catch {
419
+ // Experimental broadcast not enabled, skip silently
420
+ }
404
421
  await this.roomManager.leaveRoom(roomId, client.userId);
405
422
  await this.emit("client_left_room", { clientId, roomId });
406
423
  }
@@ -422,7 +439,7 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
422
439
  if (this.presenceManager?.isEnabled() &&
423
440
  client.userId &&
424
441
  client.authenticated) {
425
- await this.updatePresenceActivity(client.userId, clientId, "message");
442
+ await this.updatePresenceActivity(client.userId, client.deviceId, "message");
426
443
  }
427
444
  switch (message.type) {
428
445
  case "authenticate":
@@ -619,6 +636,22 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
619
636
  if (!client.roomIds.has(roomId)) {
620
637
  continue;
621
638
  }
639
+ // Broadcast to other room members BEFORE removing this client
640
+ // Use experimental broadcast if enabled, otherwise silently skip
641
+ try {
642
+ await this.experimental.broadcastWhere((c) => c.roomIds.has(roomId) && c.id !== clientId, {
643
+ type: "member_left",
644
+ payload: {
645
+ roomId,
646
+ userId: client.userId,
647
+ clientId,
648
+ timestamp: Date.now(),
649
+ },
650
+ });
651
+ }
652
+ catch {
653
+ // Experimental broadcast not enabled, skip silently
654
+ }
622
655
  await this.roomManager.leaveRoom(roomId, client.userId);
623
656
  client.roomIds.delete(roomId);
624
657
  await this.emit("client_left_room", { clientId, roomId });
@@ -818,16 +851,52 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
818
851
  getClientIdsForUser(userId) {
819
852
  return Array.from(this.userClientMap.get(userId) || []);
820
853
  }
854
+ /**
855
+ * Get all client IDs for a user in a specific room
856
+ *
857
+ * @param roomId - Room identifier
858
+ * @param userId - User identifier
859
+ * @returns Array of client IDs
860
+ */
861
+ getClientIdsInRoom(roomId, userId) {
862
+ const allClientIds = this.getClientIdsForUser(userId);
863
+ return allClientIds.filter(clientId => {
864
+ const client = this.clients.get(clientId);
865
+ return client && client.roomIds.has(roomId);
866
+ });
867
+ }
821
868
  /**
822
869
  * Get device ID from client ID
823
870
  *
824
- * In Control Tower architecture, clientId === deviceId
871
+ * By default, deviceId === clientId. Use setClientDeviceId() to override
872
+ * for multi-window scenarios where multiple clients share a device.
825
873
  *
826
874
  * @param clientId - Client identifier
827
- * @returns Device ID (same as clientId)
875
+ * @returns Device ID for this client
828
876
  */
829
877
  getDeviceIdFromClientId(clientId) {
830
- return clientId;
878
+ const client = this.clients.get(clientId);
879
+ return client?.deviceId ?? clientId;
880
+ }
881
+ /**
882
+ * Set the device ID for a client
883
+ *
884
+ * Use this to associate a custom deviceId with a client, e.g., when
885
+ * the deviceId comes from a JWT token and differs from the WebSocket clientId.
886
+ * This is useful for multi-window scenarios where multiple WebSocket clients
887
+ * share the same device/user session.
888
+ *
889
+ * @param clientId - Client identifier
890
+ * @param deviceId - Device identifier to associate with this client
891
+ * @returns true if the client was found and updated, false otherwise
892
+ */
893
+ setClientDeviceId(clientId, deviceId) {
894
+ const client = this.clients.get(clientId);
895
+ if (client) {
896
+ client.deviceId = deviceId;
897
+ return true;
898
+ }
899
+ return false;
831
900
  }
832
901
  }
833
902
  exports.BaseServer = BaseServer;
@@ -1 +1 @@
1
- {"version":3,"file":"ServerBuilder.d.ts","sourceRoot":"","sources":["../../src/server/ServerBuilder.ts"],"names":[],"mappings":"AAAA,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;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,KAAK,EAAE,yBAAyB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAqB,MAAM,iBAAiB,CAAC;AAEhE,qBAAa,aAAa;IACzB,OAAO,CAAC,SAAS,CAAC,CAAoB;IACtC,OAAO,CAAC,IAAI,CAAC,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAC,CAAkB;IAClC,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,iBAAiB,CAAC,CAAsB;IAChD,OAAO,CAAC,UAAU,CAAC,CAA2B;IAC9C,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,oBAAoB,CAAC,CAA4B;IAEzD,aAAa,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI;IAKjD,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAKlC,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAK3C,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAK/C,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAK/C;;;;;;;OAOG;IACH,mBAAmB,CAAC,eAAe,EAAE,eAAe,GAAG,IAAI;IAK3D,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI;IAKxD,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAKtD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKrC,mBAAmB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAK/C;;;;;;;OAOG;IACH,wBAAwB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI;IAKnE,KAAK,IAAI,UAAU;IAkDnB;;;OAGG;IACH,OAAO,CAAC,0BAA0B;CAyClC"}
1
+ {"version":3,"file":"ServerBuilder.d.ts","sourceRoot":"","sources":["../../src/server/ServerBuilder.ts"],"names":[],"mappings":"AAAA,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;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAGnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,KAAK,EAAE,yBAAyB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAqB,MAAM,iBAAiB,CAAC;AAEhE,qBAAa,aAAa;IACzB,OAAO,CAAC,SAAS,CAAC,CAAoB;IACtC,OAAO,CAAC,IAAI,CAAC,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAC,CAAkB;IAClC,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,iBAAiB,CAAC,CAAsB;IAChD,OAAO,CAAC,UAAU,CAAC,CAA2B;IAC9C,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,oBAAoB,CAAC,CAA4B;IAEzD,aAAa,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI;IAKjD,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAKlC,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAK3C,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAK/C,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAK/C;;;;;;;OAOG;IACH,mBAAmB,CAAC,eAAe,EAAE,eAAe,GAAG,IAAI;IAK3D,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI;IAKxD,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAKtD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKrC,mBAAmB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAK/C;;;;;;;OAOG;IACH,wBAAwB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI;IAKnE,KAAK,IAAI,UAAU;IAoEnB;;;OAGG;IACH,OAAO,CAAC,0BAA0B;CAyClC"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ServerBuilder = void 0;
4
+ const ConnectedRoomManager_js_1 = require("../abstractions/ConnectedRoomManager.js");
4
5
  const DefaultPresenceManager_js_1 = require("../abstractions/DefaultPresenceManager.js");
5
6
  const BaseServer_js_1 = require("./BaseServer.js");
6
7
  class ServerBuilder {
@@ -96,6 +97,16 @@ class ServerBuilder {
96
97
  },
97
98
  };
98
99
  const server = new BaseServer_js_1.BaseServer(config);
100
+ // Auto-enhance DefaultRoomManager with broadcasting capabilities
101
+ // This happens after server construction so we have access to server methods
102
+ if (this.roomManager.constructor.name === 'DefaultRoomManager') {
103
+ // Create enhanced room manager with server callbacks
104
+ const connectedManager = new ConnectedRoomManager_js_1.ConnectedRoomManager((clientId, message) => server.sendToClient(clientId, message), (roomId, userId) => server.getClientIdsInRoom(roomId, userId));
105
+ // Copy existing room state
106
+ connectedManager.rooms = this.roomManager.rooms;
107
+ // Replace the room manager in the server
108
+ server.roomManager = connectedManager;
109
+ }
99
110
  // Auto-link presence manager to server for broadcasting
100
111
  if (this.presenceManager instanceof DefaultPresenceManager_js_1.DefaultPresenceManager) {
101
112
  this.presenceManager.setServer(server);
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Event Validation Integration for Control Tower
3
+ *
4
+ * This demonstrates how to integrate event schema validation
5
+ * into the Control Tower server for type-safe telemetry.
6
+ *
7
+ * Usage:
8
+ * 1. Install: npm install @principal-ai/principal-view-core
9
+ * 2. Import canvas and types
10
+ * 3. Create validated event emitters
11
+ * 4. Use in server event handlers
12
+ */
13
+ import type { ExtendedCanvas } from '@principal-ai/principal-view-core';
14
+ import type { WebsocketAdapter, Server, MessageHandler, AuthAdapter, ClientLifecycle, ClientResponse, NodeEmitterByName } from '@generated/client-connection-auth.types';
15
+ /**
16
+ * Control Tower Telemetry Service
17
+ *
18
+ * Provides type-safe, validated event emission for all Control Tower operations.
19
+ */
20
+ export declare class ControlTowerTelemetry {
21
+ private validator;
22
+ private canvas;
23
+ websocketAdapter: NodeEmitterByName<WebsocketAdapter.Event>;
24
+ server: NodeEmitterByName<Server.Event>;
25
+ messageHandler: NodeEmitterByName<MessageHandler.Event>;
26
+ authAdapter: NodeEmitterByName<AuthAdapter.Event>;
27
+ clientLifecycle: NodeEmitterByName<ClientLifecycle.Event>;
28
+ clientResponse: NodeEmitterByName<ClientResponse.Event>;
29
+ constructor(canvas: ExtendedCanvas, options?: {
30
+ /** Strict mode throws on validation errors (for tests) */
31
+ strict?: boolean;
32
+ /** Custom event handler */
33
+ onEvent?: (nodeId: string, eventName: string, attributes: Record<string, any>) => void;
34
+ });
35
+ /**
36
+ * Get available event names for a component
37
+ */
38
+ getEventNames(component: 'websocket-adapter' | 'server' | 'message-handler' | 'auth-adapter' | 'client-lifecycle' | 'client-response'): string[];
39
+ /**
40
+ * Validate an event manually (for testing)
41
+ */
42
+ validate(component: string, eventName: string, attributes: Record<string, any>): any;
43
+ }
44
+ /**
45
+ * Example: Integrating with BaseServer
46
+ *
47
+ * This shows how you would integrate telemetry into the BaseServer class.
48
+ */
49
+ export declare class TelemetryIntegrationExample {
50
+ private telemetry;
51
+ constructor(canvas: ExtendedCanvas);
52
+ /**
53
+ * Example: Handle WebSocket connection received
54
+ */
55
+ handleConnectionReceived(connectionId: string, origin?: string): void;
56
+ /**
57
+ * Example: Handle WebSocket connection established
58
+ */
59
+ handleConnectionEstablished(connectionId: string, transportType: string): void;
60
+ /**
61
+ * Example: Handle client connected to server
62
+ */
63
+ handleClientConnected(clientId: string, transportType: string): void;
64
+ /**
65
+ * Example: Handle client awaiting authentication
66
+ */
67
+ handleClientAwaitingAuth(clientId: string, timeoutMs?: number): void;
68
+ /**
69
+ * Example: Handle message received
70
+ */
71
+ handleMessageReceived(clientId: string, messageType: string, messageSize?: number): void;
72
+ /**
73
+ * Example: Handle message validated
74
+ */
75
+ handleMessageValidated(clientId: string, messageType: string): void;
76
+ /**
77
+ * Example: Handle message validation failure
78
+ */
79
+ handleMessageValidationFailed(clientId: string, messageType: string, errorMessage: string, errorField?: string): void;
80
+ /**
81
+ * Example: Handle auth validation started
82
+ */
83
+ handleAuthValidationStarted(clientId: string, authMethod?: string): void;
84
+ /**
85
+ * Example: Handle auth validation success
86
+ */
87
+ handleAuthValidationSuccess(clientId: string, userId: string, authMethod?: string, duration?: number): void;
88
+ /**
89
+ * Example: Handle auth validation failure
90
+ */
91
+ handleAuthValidationFailed(clientId: string, errorCode: string, errorMessage: string): void;
92
+ /**
93
+ * Example: Handle client state changed
94
+ */
95
+ handleClientStateChanged(clientId: string, fromState: string, toState: string, duration?: number): void;
96
+ /**
97
+ * Example: Handle client authenticated
98
+ */
99
+ handleClientAuthenticated(clientId: string, userId: string, totalDuration?: number): void;
100
+ /**
101
+ * Example: Handle client disconnected
102
+ */
103
+ handleClientDisconnected(clientId: string, reason: string, sessionDuration: number, wasAuthenticated: boolean): void;
104
+ /**
105
+ * Example: Handle response sent to client
106
+ */
107
+ handleResponseSent(clientId: string, responseType: string, size?: number): void;
108
+ /**
109
+ * Example: Handle response send failure
110
+ */
111
+ handleResponseSendFailed(clientId: string, errorMessage: string): void;
112
+ }
113
+ /**
114
+ * Load canvas from file
115
+ */
116
+ export declare function loadControlTowerCanvas(): ExtendedCanvas;
117
+ /**
118
+ * Create telemetry service instance
119
+ *
120
+ * @example
121
+ * ```typescript
122
+ * const telemetry = createTelemetryService();
123
+ *
124
+ * telemetry.clientLifecycle('client.connected', {
125
+ * 'client.id': 'abc123',
126
+ * 'transport.type': 'websocket',
127
+ * 'connection.time': Date.now(),
128
+ * });
129
+ * ```
130
+ */
131
+ export declare function createTelemetryService(options?: {
132
+ strict?: boolean;
133
+ onEvent?: (nodeId: string, eventName: string, attributes: Record<string, any>) => void;
134
+ }): ControlTowerTelemetry;
135
+ //# sourceMappingURL=EventValidationIntegration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventValidationIntegration.d.ts","sourceRoot":"","sources":["../../src/telemetry/EventValidationIntegration.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ /**
3
+ * Event Validation Integration for Control Tower
4
+ *
5
+ * This demonstrates how to integrate event schema validation
6
+ * into the Control Tower server for type-safe telemetry.
7
+ *
8
+ * Usage:
9
+ * 1. Install: npm install @principal-ai/principal-view-core
10
+ * 2. Import canvas and types
11
+ * 3. Create validated event emitters
12
+ * 4. Use in server event handlers
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.TelemetryIntegrationExample = exports.ControlTowerTelemetry = void 0;
16
+ exports.loadControlTowerCanvas = loadControlTowerCanvas;
17
+ exports.createTelemetryService = createTelemetryService;
18
+ const principal_view_core_1 = require("@principal-ai/principal-view-core");
19
+ /**
20
+ * Control Tower Telemetry Service
21
+ *
22
+ * Provides type-safe, validated event emission for all Control Tower operations.
23
+ */
24
+ class ControlTowerTelemetry {
25
+ constructor(canvas, options = {}) {
26
+ this.canvas = canvas;
27
+ this.validator = new principal_view_core_1.EventValidator(canvas);
28
+ const defaultHandler = (nodeId, eventName, attributes) => {
29
+ // Default: log to console (replace with your telemetry backend)
30
+ console.log(`[${nodeId}] ${eventName}`, attributes);
31
+ };
32
+ const eventHandler = options.onEvent || defaultHandler;
33
+ // Create type-safe emitters for each component
34
+ this.websocketAdapter = (0, principal_view_core_1.createValidatedEmitter)(this.validator, 'websocket-adapter', (eventName, attributes) => eventHandler('websocket-adapter', eventName, attributes), { strict: options.strict ?? false });
35
+ this.server = (0, principal_view_core_1.createValidatedEmitter)(this.validator, 'server', (eventName, attributes) => eventHandler('server', eventName, attributes), { strict: options.strict ?? false });
36
+ this.messageHandler = (0, principal_view_core_1.createValidatedEmitter)(this.validator, 'message-handler', (eventName, attributes) => eventHandler('message-handler', eventName, attributes), { strict: options.strict ?? false });
37
+ this.authAdapter = (0, principal_view_core_1.createValidatedEmitter)(this.validator, 'auth-adapter', (eventName, attributes) => eventHandler('auth-adapter', eventName, attributes), { strict: options.strict ?? false });
38
+ this.clientLifecycle = (0, principal_view_core_1.createValidatedEmitter)(this.validator, 'client-lifecycle', (eventName, attributes) => eventHandler('client-lifecycle', eventName, attributes), { strict: options.strict ?? false });
39
+ this.clientResponse = (0, principal_view_core_1.createValidatedEmitter)(this.validator, 'client-response', (eventName, attributes) => eventHandler('client-response', eventName, attributes), { strict: options.strict ?? false });
40
+ }
41
+ /**
42
+ * Get available event names for a component
43
+ */
44
+ getEventNames(component) {
45
+ return this.validator.getNodeEventNames(component);
46
+ }
47
+ /**
48
+ * Validate an event manually (for testing)
49
+ */
50
+ validate(component, eventName, attributes) {
51
+ return this.validator.validate(component, eventName, attributes);
52
+ }
53
+ }
54
+ exports.ControlTowerTelemetry = ControlTowerTelemetry;
55
+ /**
56
+ * Example: Integrating with BaseServer
57
+ *
58
+ * This shows how you would integrate telemetry into the BaseServer class.
59
+ */
60
+ class TelemetryIntegrationExample {
61
+ constructor(canvas) {
62
+ // Create telemetry service
63
+ this.telemetry = new ControlTowerTelemetry(canvas, {
64
+ strict: false, // Permissive in production
65
+ onEvent: (nodeId, eventName, attributes) => {
66
+ // Send to your telemetry backend (OpenTelemetry, DataDog, etc.)
67
+ console.log(`[TELEMETRY] ${nodeId}.${eventName}`, attributes);
68
+ },
69
+ });
70
+ }
71
+ /**
72
+ * Example: Handle WebSocket connection received
73
+ */
74
+ handleConnectionReceived(connectionId, origin) {
75
+ this.telemetry.websocketAdapter('connection.received', {
76
+ 'connection.id': connectionId,
77
+ 'connection.origin': origin,
78
+ });
79
+ }
80
+ /**
81
+ * Example: Handle WebSocket connection established
82
+ */
83
+ handleConnectionEstablished(connectionId, transportType) {
84
+ this.telemetry.websocketAdapter('connection.established', {
85
+ 'connection.id': connectionId,
86
+ 'transport.type': transportType,
87
+ });
88
+ }
89
+ /**
90
+ * Example: Handle client connected to server
91
+ */
92
+ handleClientConnected(clientId, transportType) {
93
+ this.telemetry.server('client.connected', {
94
+ 'client.id': clientId,
95
+ 'transport.type': transportType,
96
+ 'connection.timestamp': Date.now(),
97
+ });
98
+ }
99
+ /**
100
+ * Example: Handle client awaiting authentication
101
+ */
102
+ handleClientAwaitingAuth(clientId, timeoutMs) {
103
+ this.telemetry.server('client.awaiting_auth', {
104
+ 'client.id': clientId,
105
+ 'auth.timeout_ms': timeoutMs,
106
+ });
107
+ }
108
+ /**
109
+ * Example: Handle message received
110
+ */
111
+ handleMessageReceived(clientId, messageType, messageSize) {
112
+ this.telemetry.messageHandler('message.received', {
113
+ 'client.id': clientId,
114
+ 'message.type': messageType,
115
+ 'message.size_bytes': messageSize,
116
+ });
117
+ }
118
+ /**
119
+ * Example: Handle message validated
120
+ */
121
+ handleMessageValidated(clientId, messageType) {
122
+ this.telemetry.messageHandler('message.validated', {
123
+ 'client.id': clientId,
124
+ 'message.type': messageType,
125
+ });
126
+ }
127
+ /**
128
+ * Example: Handle message validation failure
129
+ */
130
+ handleMessageValidationFailed(clientId, messageType, errorMessage, errorField) {
131
+ this.telemetry.messageHandler('message.validation_failed', {
132
+ 'client.id': clientId,
133
+ 'message.type': messageType,
134
+ 'validation.error': errorMessage,
135
+ 'validation.field': errorField,
136
+ });
137
+ }
138
+ /**
139
+ * Example: Handle auth validation started
140
+ */
141
+ handleAuthValidationStarted(clientId, authMethod) {
142
+ this.telemetry.authAdapter('auth.validation_started', {
143
+ 'client.id': clientId,
144
+ 'auth.method': authMethod,
145
+ });
146
+ }
147
+ /**
148
+ * Example: Handle auth validation success
149
+ */
150
+ handleAuthValidationSuccess(clientId, userId, authMethod, duration) {
151
+ this.telemetry.authAdapter('auth.validation_success', {
152
+ 'client.id': clientId,
153
+ 'user.id': userId,
154
+ 'auth.method': authMethod,
155
+ 'validation.duration_ms': duration,
156
+ });
157
+ }
158
+ /**
159
+ * Example: Handle auth validation failure
160
+ */
161
+ handleAuthValidationFailed(clientId, errorCode, errorMessage) {
162
+ this.telemetry.authAdapter('auth.validation_failed', {
163
+ 'client.id': clientId,
164
+ 'auth.error_code': errorCode,
165
+ 'auth.error_message': errorMessage,
166
+ });
167
+ }
168
+ /**
169
+ * Example: Handle client state changed
170
+ */
171
+ handleClientStateChanged(clientId, fromState, toState, duration) {
172
+ this.telemetry.clientLifecycle('client.state_changed', {
173
+ 'client.id': clientId,
174
+ 'state.from': fromState,
175
+ 'state.to': toState,
176
+ 'state.duration_ms': duration,
177
+ });
178
+ }
179
+ /**
180
+ * Example: Handle client authenticated
181
+ */
182
+ handleClientAuthenticated(clientId, userId, totalDuration) {
183
+ // ✅ Type-safe: TypeScript knows exact attributes for this event
184
+ this.telemetry.clientLifecycle('client.authenticated', {
185
+ 'client.id': clientId,
186
+ 'user.id': userId,
187
+ 'connection.total_duration_ms': totalDuration,
188
+ });
189
+ // ❌ TypeScript error: missing required field
190
+ // this.telemetry.clientLifecycle('client.authenticated', {
191
+ // 'client.id': clientId,
192
+ // // Missing 'user.id' - TypeScript error!
193
+ // });
194
+ }
195
+ /**
196
+ * Example: Handle client disconnected
197
+ */
198
+ handleClientDisconnected(clientId, reason, sessionDuration, wasAuthenticated) {
199
+ this.telemetry.clientLifecycle('client.disconnected', {
200
+ 'client.id': clientId,
201
+ 'disconnect.reason': reason,
202
+ 'session.total_duration_ms': sessionDuration,
203
+ 'client.was_authenticated': wasAuthenticated,
204
+ });
205
+ }
206
+ /**
207
+ * Example: Handle response sent to client
208
+ */
209
+ handleResponseSent(clientId, responseType, size) {
210
+ this.telemetry.clientResponse('response.sent', {
211
+ 'client.id': clientId,
212
+ 'response.type': responseType,
213
+ 'response.size_bytes': size,
214
+ });
215
+ }
216
+ /**
217
+ * Example: Handle response send failure
218
+ */
219
+ handleResponseSendFailed(clientId, errorMessage) {
220
+ this.telemetry.clientResponse('response.send_failed', {
221
+ 'client.id': clientId,
222
+ 'error.message': errorMessage,
223
+ });
224
+ }
225
+ }
226
+ exports.TelemetryIntegrationExample = TelemetryIntegrationExample;
227
+ /**
228
+ * Load canvas from file
229
+ */
230
+ function loadControlTowerCanvas() {
231
+ const fs = require('fs');
232
+ const path = require('path');
233
+ const canvasPath = path.join(__dirname, '../../.principal-views/client-connection-auth.otel.canvas');
234
+ return JSON.parse(fs.readFileSync(canvasPath, 'utf-8'));
235
+ }
236
+ /**
237
+ * Create telemetry service instance
238
+ *
239
+ * @example
240
+ * ```typescript
241
+ * const telemetry = createTelemetryService();
242
+ *
243
+ * telemetry.clientLifecycle('client.connected', {
244
+ * 'client.id': 'abc123',
245
+ * 'transport.type': 'websocket',
246
+ * 'connection.time': Date.now(),
247
+ * });
248
+ * ```
249
+ */
250
+ function createTelemetryService(options) {
251
+ const canvas = loadControlTowerCanvas();
252
+ return new ControlTowerTelemetry(canvas, options);
253
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Event Validation Integration Tests
3
+ *
4
+ * Demonstrates the Control Tower telemetry integration working end-to-end.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=EventValidationIntegration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventValidationIntegration.test.d.ts","sourceRoot":"","sources":["../../src/telemetry/EventValidationIntegration.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}