@principal-ai/control-tower-core 0.2.1 → 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.
- package/dist/generated/client-connection-auth.types.d.ts +312 -0
- package/dist/generated/client-connection-auth.types.d.ts.map +1 -0
- package/dist/generated/client-connection-auth.types.js +11 -0
- package/dist/generated/control-tower-execution.types.d.ts +445 -0
- package/dist/generated/control-tower-execution.types.d.ts.map +1 -0
- package/dist/generated/control-tower-execution.types.js +11 -0
- package/dist/index.js.map +3 -3
- package/dist/index.mjs +39 -6
- package/dist/index.mjs.map +3 -3
- package/dist/server/BaseServer.d.ts +22 -2
- package/dist/server/BaseServer.d.ts.map +1 -1
- package/dist/server/BaseServer.js +63 -8
- package/dist/telemetry/EventValidationIntegration.d.ts +135 -0
- package/dist/telemetry/EventValidationIntegration.d.ts.map +1 -0
- package/dist/telemetry/EventValidationIntegration.js +253 -0
- package/dist/telemetry/EventValidationIntegration.test.d.ts +7 -0
- package/dist/telemetry/EventValidationIntegration.test.d.ts.map +1 -0
- package/dist/telemetry/EventValidationIntegration.test.js +322 -0
- package/dist/telemetry/TelemetryCapture.d.ts +268 -0
- package/dist/telemetry/TelemetryCapture.d.ts.map +1 -0
- package/dist/telemetry/TelemetryCapture.js +263 -0
- package/dist/telemetry/TelemetryCapture.test.d.ts +7 -0
- package/dist/telemetry/TelemetryCapture.test.d.ts.map +1 -0
- package/dist/telemetry/TelemetryCapture.test.js +396 -0
- package/dist/telemetry-example.d.ts +33 -0
- package/dist/telemetry-example.d.ts.map +1 -0
- package/dist/telemetry-example.js +124 -0
- package/package.json +1 -1
- package/dist/adapters/websocket/WebSocketTransportAdapter.d.ts +0 -60
- package/dist/adapters/websocket/WebSocketTransportAdapter.d.ts.map +0 -1
- package/dist/adapters/websocket/WebSocketTransportAdapter.js +0 -386
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Event Validation Integration Tests
|
|
4
|
+
*
|
|
5
|
+
* Demonstrates the Control Tower telemetry integration working end-to-end.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const bun_test_1 = require("bun:test");
|
|
9
|
+
const EventValidationIntegration_1 = require("./EventValidationIntegration");
|
|
10
|
+
(0, bun_test_1.describe)('Control Tower Telemetry Integration', () => {
|
|
11
|
+
let telemetry;
|
|
12
|
+
let events;
|
|
13
|
+
(0, bun_test_1.beforeEach)(() => {
|
|
14
|
+
events = [];
|
|
15
|
+
const canvas = (0, EventValidationIntegration_1.loadControlTowerCanvas)();
|
|
16
|
+
telemetry = new EventValidationIntegration_1.ControlTowerTelemetry(canvas, {
|
|
17
|
+
strict: true, // Strict mode for tests
|
|
18
|
+
onEvent: (nodeId, eventName, attributes) => {
|
|
19
|
+
events.push({ nodeId, eventName, attributes });
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
(0, bun_test_1.describe)('Message Handler Events', () => {
|
|
24
|
+
(0, bun_test_1.test)('should emit message.received event', () => {
|
|
25
|
+
telemetry.messageHandler('message.received', {
|
|
26
|
+
'client.id': 'client-123',
|
|
27
|
+
'message.type': 'authenticate',
|
|
28
|
+
'message.size': 256,
|
|
29
|
+
});
|
|
30
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
31
|
+
(0, bun_test_1.expect)(events[0].nodeId).toBe('message-handler');
|
|
32
|
+
(0, bun_test_1.expect)(events[0].eventName).toBe('message.received');
|
|
33
|
+
(0, bun_test_1.expect)(events[0].attributes['client.id']).toBe('client-123');
|
|
34
|
+
});
|
|
35
|
+
(0, bun_test_1.test)('should emit message.validated event', () => {
|
|
36
|
+
telemetry.messageHandler('message.validated', {
|
|
37
|
+
'client.id': 'client-123',
|
|
38
|
+
'message.type': 'join_room',
|
|
39
|
+
});
|
|
40
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
41
|
+
(0, bun_test_1.expect)(events[0].eventName).toBe('message.validated');
|
|
42
|
+
});
|
|
43
|
+
(0, bun_test_1.test)('should emit message.validation_failed event', () => {
|
|
44
|
+
telemetry.messageHandler('message.validation_failed', {
|
|
45
|
+
'client.id': 'client-123',
|
|
46
|
+
'message.type': 'broadcast_event',
|
|
47
|
+
'error.message': 'Missing required field: roomId',
|
|
48
|
+
'error.field': 'roomId',
|
|
49
|
+
});
|
|
50
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
51
|
+
(0, bun_test_1.expect)(events[0].attributes['error.message']).toBe('Missing required field: roomId');
|
|
52
|
+
});
|
|
53
|
+
(0, bun_test_1.test)('should throw on missing required fields in strict mode', () => {
|
|
54
|
+
(0, bun_test_1.expect)(() => {
|
|
55
|
+
telemetry.messageHandler('message.received', {
|
|
56
|
+
'client.id': 'client-123',
|
|
57
|
+
// Missing 'message.type' - required field!
|
|
58
|
+
});
|
|
59
|
+
}).toThrow();
|
|
60
|
+
});
|
|
61
|
+
(0, bun_test_1.test)('should throw on wrong field types', () => {
|
|
62
|
+
(0, bun_test_1.expect)(() => {
|
|
63
|
+
telemetry.messageHandler('message.received', {
|
|
64
|
+
'client.id': 'client-123',
|
|
65
|
+
'message.type': 'test',
|
|
66
|
+
'message.size': 'large', // Should be number!
|
|
67
|
+
});
|
|
68
|
+
}).toThrow();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
(0, bun_test_1.describe)('Client Lifecycle Events', () => {
|
|
72
|
+
(0, bun_test_1.test)('should emit client.connected event', () => {
|
|
73
|
+
telemetry.clientLifecycle('client.connected', {
|
|
74
|
+
'client.id': 'client-456',
|
|
75
|
+
'transport.type': 'websocket',
|
|
76
|
+
'connection.time': Date.now(),
|
|
77
|
+
});
|
|
78
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
79
|
+
(0, bun_test_1.expect)(events[0].eventName).toBe('client.connected');
|
|
80
|
+
(0, bun_test_1.expect)(events[0].attributes['transport.type']).toBe('websocket');
|
|
81
|
+
});
|
|
82
|
+
(0, bun_test_1.test)('should emit client.authenticated event', () => {
|
|
83
|
+
telemetry.clientLifecycle('client.authenticated', {
|
|
84
|
+
'client.id': 'client-456',
|
|
85
|
+
'user.id': 'user-789',
|
|
86
|
+
'auth.method': 'jwt',
|
|
87
|
+
});
|
|
88
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
89
|
+
(0, bun_test_1.expect)(events[0].attributes['user.id']).toBe('user-789');
|
|
90
|
+
});
|
|
91
|
+
(0, bun_test_1.test)('should emit client.disconnected event with optional fields', () => {
|
|
92
|
+
telemetry.clientLifecycle('client.disconnected', {
|
|
93
|
+
'client.id': 'client-456',
|
|
94
|
+
'disconnect.reason': 'timeout',
|
|
95
|
+
'session.duration': 120000,
|
|
96
|
+
});
|
|
97
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
98
|
+
(0, bun_test_1.expect)(events[0].attributes['session.duration']).toBe(120000);
|
|
99
|
+
});
|
|
100
|
+
(0, bun_test_1.test)('should allow omitting optional fields', () => {
|
|
101
|
+
telemetry.clientLifecycle('client.disconnected', {
|
|
102
|
+
'client.id': 'client-456',
|
|
103
|
+
// Optional fields omitted
|
|
104
|
+
});
|
|
105
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
106
|
+
(0, bun_test_1.expect)(events[0].attributes).not.toHaveProperty('disconnect.reason');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
(0, bun_test_1.describe)('Room Manager Events', () => {
|
|
110
|
+
(0, bun_test_1.test)('should emit room.created event', () => {
|
|
111
|
+
telemetry.roomManager('room.created', {
|
|
112
|
+
'room.id': 'room-001',
|
|
113
|
+
'creator.id': 'client-123',
|
|
114
|
+
'config.max_events': 1000,
|
|
115
|
+
});
|
|
116
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
117
|
+
(0, bun_test_1.expect)(events[0].attributes['room.id']).toBe('room-001');
|
|
118
|
+
});
|
|
119
|
+
(0, bun_test_1.test)('should emit room.client_joined event', () => {
|
|
120
|
+
telemetry.roomManager('room.client_joined', {
|
|
121
|
+
'room.id': 'room-001',
|
|
122
|
+
'client.id': 'client-456',
|
|
123
|
+
'room.client_count': 3,
|
|
124
|
+
});
|
|
125
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
126
|
+
(0, bun_test_1.expect)(events[0].attributes['room.client_count']).toBe(3);
|
|
127
|
+
});
|
|
128
|
+
(0, bun_test_1.test)('should emit room.state_serialized event', () => {
|
|
129
|
+
telemetry.roomManager('room.state_serialized', {
|
|
130
|
+
'room.id': 'room-001',
|
|
131
|
+
'client.id': 'client-456',
|
|
132
|
+
'state.event_count': 25,
|
|
133
|
+
});
|
|
134
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
135
|
+
(0, bun_test_1.expect)(events[0].attributes['state.event_count']).toBe(25);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
(0, bun_test_1.describe)('Event Broadcaster Events', () => {
|
|
139
|
+
(0, bun_test_1.test)('should emit event.broadcast event', () => {
|
|
140
|
+
telemetry.eventBroadcaster('event.broadcast', {
|
|
141
|
+
'room.id': 'room-001',
|
|
142
|
+
'event.type': 'file_change',
|
|
143
|
+
'recipient.count': 5,
|
|
144
|
+
});
|
|
145
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
146
|
+
(0, bun_test_1.expect)(events[0].attributes['event.type']).toBe('file_change');
|
|
147
|
+
});
|
|
148
|
+
(0, bun_test_1.test)('should emit event.history_added event', () => {
|
|
149
|
+
telemetry.eventBroadcaster('event.history_added', {
|
|
150
|
+
'room.id': 'room-001',
|
|
151
|
+
'event.type': 'commit',
|
|
152
|
+
'history.size': 150,
|
|
153
|
+
'history.truncated': false,
|
|
154
|
+
});
|
|
155
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
156
|
+
(0, bun_test_1.expect)(events[0].attributes['history.truncated']).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
(0, bun_test_1.describe)('Lock Manager Events', () => {
|
|
160
|
+
(0, bun_test_1.test)('should emit lock.requested event', () => {
|
|
161
|
+
telemetry.lockManager('lock.requested', {
|
|
162
|
+
'lock.id': 'file:/path/to/file',
|
|
163
|
+
'client.id': 'client-123',
|
|
164
|
+
'lock.type': 'file',
|
|
165
|
+
'lock.priority': 'high',
|
|
166
|
+
});
|
|
167
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
168
|
+
(0, bun_test_1.expect)(events[0].attributes['lock.priority']).toBe('high');
|
|
169
|
+
});
|
|
170
|
+
(0, bun_test_1.test)('should emit lock.acquired event', () => {
|
|
171
|
+
telemetry.lockManager('lock.acquired', {
|
|
172
|
+
'lock.id': 'file:/path/to/file',
|
|
173
|
+
'client.id': 'client-123',
|
|
174
|
+
'lock.type': 'file',
|
|
175
|
+
'queue.wait_time': 500,
|
|
176
|
+
});
|
|
177
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
178
|
+
(0, bun_test_1.expect)(events[0].attributes['queue.wait_time']).toBe(500);
|
|
179
|
+
});
|
|
180
|
+
(0, bun_test_1.test)('should emit lock.queued event', () => {
|
|
181
|
+
telemetry.lockManager('lock.queued', {
|
|
182
|
+
'lock.id': 'file:/path/to/file',
|
|
183
|
+
'client.id': 'client-456',
|
|
184
|
+
'queue.position': 2,
|
|
185
|
+
'queue.size': 5,
|
|
186
|
+
});
|
|
187
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
188
|
+
(0, bun_test_1.expect)(events[0].attributes['queue.position']).toBe(2);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
(0, bun_test_1.describe)('Presence Manager Events', () => {
|
|
192
|
+
(0, bun_test_1.test)('should emit presence.device_connected event', () => {
|
|
193
|
+
telemetry.presenceManager('presence.device_connected', {
|
|
194
|
+
'user.id': 'user-789',
|
|
195
|
+
'device.id': 'device-001',
|
|
196
|
+
'device.info': 'MacBook Pro, Chrome 120',
|
|
197
|
+
});
|
|
198
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
199
|
+
(0, bun_test_1.expect)(events[0].attributes['device.info']).toContain('MacBook Pro');
|
|
200
|
+
});
|
|
201
|
+
(0, bun_test_1.test)('should emit presence.status_changed event', () => {
|
|
202
|
+
telemetry.presenceManager('presence.status_changed', {
|
|
203
|
+
'user.id': 'user-789',
|
|
204
|
+
'status.from': 'away',
|
|
205
|
+
'status.to': 'online',
|
|
206
|
+
'device.count': 2,
|
|
207
|
+
});
|
|
208
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
209
|
+
(0, bun_test_1.expect)(events[0].attributes['status.from']).toBe('away');
|
|
210
|
+
(0, bun_test_1.expect)(events[0].attributes['status.to']).toBe('online');
|
|
211
|
+
});
|
|
212
|
+
(0, bun_test_1.test)('should emit presence.heartbeat_received event', () => {
|
|
213
|
+
telemetry.presenceManager('presence.heartbeat_received', {
|
|
214
|
+
'user.id': 'user-789',
|
|
215
|
+
'device.id': 'device-001',
|
|
216
|
+
'activity.type': 'typing',
|
|
217
|
+
});
|
|
218
|
+
(0, bun_test_1.expect)(events).toHaveLength(1);
|
|
219
|
+
(0, bun_test_1.expect)(events[0].attributes['activity.type']).toBe('typing');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
(0, bun_test_1.describe)('Integration Example', () => {
|
|
223
|
+
(0, bun_test_1.test)('should demonstrate full workflow', () => {
|
|
224
|
+
// Track events emitted by the example
|
|
225
|
+
const exampleEvents = [];
|
|
226
|
+
// Create telemetry with custom event handler
|
|
227
|
+
const canvas = (0, EventValidationIntegration_1.loadControlTowerCanvas)();
|
|
228
|
+
const telemetry = new EventValidationIntegration_1.ControlTowerTelemetry(canvas, {
|
|
229
|
+
strict: false,
|
|
230
|
+
onEvent: (nodeId, eventName, attributes) => {
|
|
231
|
+
exampleEvents.push({ nodeId, eventName, attributes });
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
// Simulate client connection flow using telemetry directly
|
|
235
|
+
telemetry.clientLifecycle('client.connected', {
|
|
236
|
+
'client.id': 'client-123',
|
|
237
|
+
'transport.type': 'websocket',
|
|
238
|
+
'connection.time': Date.now(),
|
|
239
|
+
});
|
|
240
|
+
telemetry.clientLifecycle('client.authenticated', {
|
|
241
|
+
'client.id': 'client-123',
|
|
242
|
+
'user.id': 'user-456',
|
|
243
|
+
'auth.method': 'jwt',
|
|
244
|
+
});
|
|
245
|
+
telemetry.roomManager('room.created', {
|
|
246
|
+
'room.id': 'room-001',
|
|
247
|
+
'creator.id': 'client-123',
|
|
248
|
+
'config.max_events': 1000,
|
|
249
|
+
});
|
|
250
|
+
telemetry.roomManager('room.client_joined', {
|
|
251
|
+
'room.id': 'room-001',
|
|
252
|
+
'client.id': 'client-123',
|
|
253
|
+
'room.client_count': 1,
|
|
254
|
+
});
|
|
255
|
+
telemetry.eventBroadcaster('event.broadcast', {
|
|
256
|
+
'room.id': 'room-001',
|
|
257
|
+
'event.type': 'file_change',
|
|
258
|
+
'recipient.count': 1,
|
|
259
|
+
});
|
|
260
|
+
telemetry.lockManager('lock.acquired', {
|
|
261
|
+
'lock.id': 'file:/src/index.ts',
|
|
262
|
+
'client.id': 'client-123',
|
|
263
|
+
'lock.type': 'file',
|
|
264
|
+
'queue.wait_time': 100,
|
|
265
|
+
});
|
|
266
|
+
telemetry.presenceManager('presence.status_changed', {
|
|
267
|
+
'user.id': 'user-456',
|
|
268
|
+
'status.from': 'offline',
|
|
269
|
+
'status.to': 'online',
|
|
270
|
+
'device.count': 1,
|
|
271
|
+
});
|
|
272
|
+
// Verify all events were emitted
|
|
273
|
+
(0, bun_test_1.expect)(exampleEvents.length).toBe(7);
|
|
274
|
+
(0, bun_test_1.expect)(exampleEvents[0].nodeId).toBe('client-lifecycle');
|
|
275
|
+
(0, bun_test_1.expect)(exampleEvents[0].eventName).toBe('client.connected');
|
|
276
|
+
(0, bun_test_1.expect)(exampleEvents[6].nodeId).toBe('presence-manager');
|
|
277
|
+
(0, bun_test_1.expect)(exampleEvents[6].eventName).toBe('presence.status_changed');
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
(0, bun_test_1.describe)('Event Name Discovery', () => {
|
|
281
|
+
(0, bun_test_1.test)('should list available event names for each component', () => {
|
|
282
|
+
const messageEvents = telemetry.getEventNames('message-handler');
|
|
283
|
+
(0, bun_test_1.expect)(messageEvents).toContain('message.received');
|
|
284
|
+
(0, bun_test_1.expect)(messageEvents).toContain('message.validated');
|
|
285
|
+
(0, bun_test_1.expect)(messageEvents).toContain('message.validation_failed');
|
|
286
|
+
const lifecycleEvents = telemetry.getEventNames('client-lifecycle');
|
|
287
|
+
(0, bun_test_1.expect)(lifecycleEvents).toContain('client.connected');
|
|
288
|
+
(0, bun_test_1.expect)(lifecycleEvents).toContain('client.authenticated');
|
|
289
|
+
(0, bun_test_1.expect)(lifecycleEvents).toContain('client.disconnected');
|
|
290
|
+
const roomEvents = telemetry.getEventNames('room-manager');
|
|
291
|
+
(0, bun_test_1.expect)(roomEvents).toContain('room.created');
|
|
292
|
+
(0, bun_test_1.expect)(roomEvents).toContain('room.client_joined');
|
|
293
|
+
const lockEvents = telemetry.getEventNames('lock-manager');
|
|
294
|
+
(0, bun_test_1.expect)(lockEvents).toContain('lock.requested');
|
|
295
|
+
(0, bun_test_1.expect)(lockEvents).toContain('lock.acquired');
|
|
296
|
+
(0, bun_test_1.expect)(lockEvents).toContain('lock.released');
|
|
297
|
+
const presenceEvents = telemetry.getEventNames('presence-manager');
|
|
298
|
+
(0, bun_test_1.expect)(presenceEvents).toContain('presence.device_connected');
|
|
299
|
+
(0, bun_test_1.expect)(presenceEvents).toContain('presence.status_changed');
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
(0, bun_test_1.describe)('Manual Validation', () => {
|
|
303
|
+
(0, bun_test_1.test)('should validate events manually', () => {
|
|
304
|
+
const result = telemetry.validate('message-handler', 'message.received', {
|
|
305
|
+
'client.id': 'client-123',
|
|
306
|
+
'message.type': 'test',
|
|
307
|
+
'message.size': 100,
|
|
308
|
+
});
|
|
309
|
+
(0, bun_test_1.expect)(result.valid).toBe(true);
|
|
310
|
+
(0, bun_test_1.expect)(result.errors).toHaveLength(0);
|
|
311
|
+
});
|
|
312
|
+
(0, bun_test_1.test)('should detect validation errors', () => {
|
|
313
|
+
const result = telemetry.validate('message-handler', 'message.received', {
|
|
314
|
+
'client.id': 'client-123',
|
|
315
|
+
// Missing 'message.type' - required field
|
|
316
|
+
});
|
|
317
|
+
(0, bun_test_1.expect)(result.valid).toBe(false);
|
|
318
|
+
(0, bun_test_1.expect)(result.errors.length).toBeGreaterThan(0);
|
|
319
|
+
(0, bun_test_1.expect)(result.errors[0]).toContain('message.type');
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
});
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telemetry Capture and Replay System
|
|
3
|
+
*
|
|
4
|
+
* Allows capturing telemetry events during test execution and replaying them later.
|
|
5
|
+
* This enables:
|
|
6
|
+
* - Saving telemetry as test artifacts
|
|
7
|
+
* - Loading telemetry for visualization
|
|
8
|
+
* - Not depending on live emission during tests
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* A captured telemetry event with metadata
|
|
12
|
+
*/
|
|
13
|
+
export interface CapturedEvent {
|
|
14
|
+
/** Timestamp when event was emitted */
|
|
15
|
+
timestamp: number;
|
|
16
|
+
/** Node ID (component) that emitted the event */
|
|
17
|
+
nodeId: string;
|
|
18
|
+
/** Event name */
|
|
19
|
+
eventName: string;
|
|
20
|
+
/** Event attributes */
|
|
21
|
+
attributes: Record<string, any>;
|
|
22
|
+
/** Sequence number within the capture session */
|
|
23
|
+
sequenceNumber: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Metadata about a telemetry capture session
|
|
27
|
+
*/
|
|
28
|
+
export interface CaptureMetadata {
|
|
29
|
+
/** Unique ID for this capture session */
|
|
30
|
+
sessionId: string;
|
|
31
|
+
/** Start time of capture */
|
|
32
|
+
startTime: number;
|
|
33
|
+
/** End time of capture */
|
|
34
|
+
endTime?: number;
|
|
35
|
+
/** Test name or description */
|
|
36
|
+
testName?: string;
|
|
37
|
+
/** Test category/suite */
|
|
38
|
+
testCategory?: string;
|
|
39
|
+
/** Canvas used for validation */
|
|
40
|
+
canvasVersion?: string;
|
|
41
|
+
/** Additional metadata */
|
|
42
|
+
tags?: Record<string, string>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* OpenTelemetry Span format
|
|
46
|
+
*/
|
|
47
|
+
export interface TelemetrySpan {
|
|
48
|
+
/** Unique span identifier */
|
|
49
|
+
id: string;
|
|
50
|
+
/** Span name */
|
|
51
|
+
name: string;
|
|
52
|
+
/** Start time in milliseconds */
|
|
53
|
+
startTime: number;
|
|
54
|
+
/** End time in milliseconds */
|
|
55
|
+
endTime?: number;
|
|
56
|
+
/** Duration in milliseconds */
|
|
57
|
+
duration?: number;
|
|
58
|
+
/** Span status */
|
|
59
|
+
status: 'OK' | 'ERROR';
|
|
60
|
+
/** Error message if status is ERROR */
|
|
61
|
+
errorMessage?: string;
|
|
62
|
+
/** Span attributes */
|
|
63
|
+
attributes: Record<string, string | number | boolean>;
|
|
64
|
+
/** Events within this span */
|
|
65
|
+
events: Array<{
|
|
66
|
+
/** Event timestamp */
|
|
67
|
+
time: number;
|
|
68
|
+
/** Event name */
|
|
69
|
+
name: string;
|
|
70
|
+
/** Event attributes */
|
|
71
|
+
attributes: Record<string, string | number | boolean>;
|
|
72
|
+
}>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Complete telemetry capture artifact in OpenTelemetry format
|
|
76
|
+
*/
|
|
77
|
+
export interface TelemetryArtifact {
|
|
78
|
+
/** Artifact metadata */
|
|
79
|
+
metadata?: {
|
|
80
|
+
/** Canvas name this execution is associated with */
|
|
81
|
+
canvasName?: string;
|
|
82
|
+
/** Export timestamp */
|
|
83
|
+
exportedAt?: string;
|
|
84
|
+
/** Source of execution (e.g., test name) */
|
|
85
|
+
source?: string;
|
|
86
|
+
/** Test framework used */
|
|
87
|
+
framework?: string;
|
|
88
|
+
/** Execution status */
|
|
89
|
+
status?: 'success' | 'error' | 'OK';
|
|
90
|
+
/** Test category/suite */
|
|
91
|
+
testCategory?: string;
|
|
92
|
+
/** Additional tags */
|
|
93
|
+
tags?: Record<string, string>;
|
|
94
|
+
};
|
|
95
|
+
/** Spans containing events */
|
|
96
|
+
spans: TelemetrySpan[];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Telemetry Capture
|
|
100
|
+
*
|
|
101
|
+
* Captures telemetry events in memory and exports them as artifacts.
|
|
102
|
+
*/
|
|
103
|
+
export declare class TelemetryCapture {
|
|
104
|
+
private events;
|
|
105
|
+
private metadata;
|
|
106
|
+
private sequenceCounter;
|
|
107
|
+
private startTime;
|
|
108
|
+
constructor(options?: {
|
|
109
|
+
testName?: string;
|
|
110
|
+
testCategory?: string;
|
|
111
|
+
sessionId?: string;
|
|
112
|
+
tags?: Record<string, string>;
|
|
113
|
+
});
|
|
114
|
+
/**
|
|
115
|
+
* Create an event handler for ControlTowerTelemetry
|
|
116
|
+
*/
|
|
117
|
+
createEventHandler(): (nodeId: string, eventName: string, attributes: Record<string, any>) => void;
|
|
118
|
+
/**
|
|
119
|
+
* Capture a telemetry event
|
|
120
|
+
*/
|
|
121
|
+
captureEvent(nodeId: string, eventName: string, attributes: Record<string, any>): void;
|
|
122
|
+
/**
|
|
123
|
+
* Stop capturing and finalize metadata
|
|
124
|
+
*/
|
|
125
|
+
stop(): void;
|
|
126
|
+
/**
|
|
127
|
+
* Export as artifact in OpenTelemetry spans format
|
|
128
|
+
*/
|
|
129
|
+
export(): TelemetryArtifact;
|
|
130
|
+
/**
|
|
131
|
+
* Export as JSON string
|
|
132
|
+
*/
|
|
133
|
+
exportJSON(pretty?: boolean): string;
|
|
134
|
+
/**
|
|
135
|
+
* Get events captured so far
|
|
136
|
+
*/
|
|
137
|
+
getEvents(): ReadonlyArray<CapturedEvent>;
|
|
138
|
+
/**
|
|
139
|
+
* Get event count
|
|
140
|
+
*/
|
|
141
|
+
getEventCount(): number;
|
|
142
|
+
/**
|
|
143
|
+
* Clear all captured events
|
|
144
|
+
*/
|
|
145
|
+
clear(): void;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Telemetry Player
|
|
149
|
+
*
|
|
150
|
+
* Replays captured telemetry events.
|
|
151
|
+
*/
|
|
152
|
+
export declare class TelemetryPlayer {
|
|
153
|
+
private artifact;
|
|
154
|
+
private currentIndex;
|
|
155
|
+
constructor(artifact: TelemetryArtifact);
|
|
156
|
+
/**
|
|
157
|
+
* Load artifact from JSON string
|
|
158
|
+
*/
|
|
159
|
+
static fromJSON(json: string): TelemetryPlayer;
|
|
160
|
+
/**
|
|
161
|
+
* Load artifact from file
|
|
162
|
+
*/
|
|
163
|
+
static fromFile(path: string): TelemetryPlayer;
|
|
164
|
+
/**
|
|
165
|
+
* Get artifact metadata
|
|
166
|
+
*/
|
|
167
|
+
getMetadata(): {
|
|
168
|
+
/** Canvas name this execution is associated with */
|
|
169
|
+
canvasName?: string;
|
|
170
|
+
/** Export timestamp */
|
|
171
|
+
exportedAt?: string;
|
|
172
|
+
/** Source of execution (e.g., test name) */
|
|
173
|
+
source?: string;
|
|
174
|
+
/** Test framework used */
|
|
175
|
+
framework?: string;
|
|
176
|
+
/** Execution status */
|
|
177
|
+
status?: "success" | "error" | "OK";
|
|
178
|
+
/** Test category/suite */
|
|
179
|
+
testCategory?: string;
|
|
180
|
+
/** Additional tags */
|
|
181
|
+
tags?: Record<string, string>;
|
|
182
|
+
} | undefined;
|
|
183
|
+
/**
|
|
184
|
+
* Get all spans
|
|
185
|
+
*/
|
|
186
|
+
getSpans(): ReadonlyArray<TelemetrySpan>;
|
|
187
|
+
/**
|
|
188
|
+
* Get all events from all spans
|
|
189
|
+
*/
|
|
190
|
+
getAllEvents(): Array<{
|
|
191
|
+
time: number;
|
|
192
|
+
name: string;
|
|
193
|
+
attributes?: Record<string, any>;
|
|
194
|
+
}>;
|
|
195
|
+
/**
|
|
196
|
+
* Get summary statistics
|
|
197
|
+
*/
|
|
198
|
+
getSummary(): {
|
|
199
|
+
totalSpans: number;
|
|
200
|
+
totalEvents: number;
|
|
201
|
+
status: "error" | "success" | "OK";
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Get events for a specific node
|
|
205
|
+
*/
|
|
206
|
+
getEventsByNode(nodeId: string): {
|
|
207
|
+
time: number;
|
|
208
|
+
name: string;
|
|
209
|
+
attributes?: Record<string, any>;
|
|
210
|
+
}[];
|
|
211
|
+
/**
|
|
212
|
+
* Get events by name
|
|
213
|
+
*/
|
|
214
|
+
getEventsByName(eventName: string): {
|
|
215
|
+
time: number;
|
|
216
|
+
name: string;
|
|
217
|
+
attributes?: Record<string, any>;
|
|
218
|
+
}[];
|
|
219
|
+
/**
|
|
220
|
+
* Get events in time range
|
|
221
|
+
*/
|
|
222
|
+
getEventsInRange(startTime: number, endTime: number): {
|
|
223
|
+
time: number;
|
|
224
|
+
name: string;
|
|
225
|
+
attributes?: Record<string, any>;
|
|
226
|
+
}[];
|
|
227
|
+
/**
|
|
228
|
+
* Replay events with callback
|
|
229
|
+
*/
|
|
230
|
+
replay(callback: (event: {
|
|
231
|
+
time: number;
|
|
232
|
+
name: string;
|
|
233
|
+
attributes?: Record<string, any>;
|
|
234
|
+
}) => void): void;
|
|
235
|
+
/**
|
|
236
|
+
* Replay events with timing (respects original timing)
|
|
237
|
+
*/
|
|
238
|
+
replayWithTiming(callback: (event: {
|
|
239
|
+
time: number;
|
|
240
|
+
name: string;
|
|
241
|
+
attributes?: Record<string, any>;
|
|
242
|
+
}) => void | Promise<void>): Promise<void>;
|
|
243
|
+
/**
|
|
244
|
+
* Get next event (for step-through debugging)
|
|
245
|
+
*/
|
|
246
|
+
next(): {
|
|
247
|
+
time: number;
|
|
248
|
+
name: string;
|
|
249
|
+
attributes?: Record<string, any>;
|
|
250
|
+
} | null;
|
|
251
|
+
/**
|
|
252
|
+
* Reset player to beginning
|
|
253
|
+
*/
|
|
254
|
+
reset(): void;
|
|
255
|
+
/**
|
|
256
|
+
* Check if more events available
|
|
257
|
+
*/
|
|
258
|
+
hasNext(): boolean;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Helper to save artifact to file
|
|
262
|
+
*/
|
|
263
|
+
export declare function saveTelemetryArtifact(artifact: TelemetryArtifact, path: string, pretty?: boolean): void;
|
|
264
|
+
/**
|
|
265
|
+
* Helper to load artifact from file
|
|
266
|
+
*/
|
|
267
|
+
export declare function loadTelemetryArtifact(path: string): TelemetryArtifact;
|
|
268
|
+
//# sourceMappingURL=TelemetryCapture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelemetryCapture.d.ts","sourceRoot":"","sources":["../../src/telemetry/TelemetryCapture.ts"],"names":[],"mappings":"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"}
|