@esotech/contextuate 2.0.0 → 2.1.0
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/README.md +169 -1
- package/dist/commands/claude.d.ts +21 -0
- package/dist/commands/claude.js +213 -0
- package/dist/commands/context.d.ts +1 -0
- package/dist/commands/create.d.ts +3 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +67 -6
- package/dist/commands/install.d.ts +28 -0
- package/dist/commands/install.js +116 -11
- package/dist/commands/monitor.d.ts +55 -0
- package/dist/commands/monitor.js +1007 -0
- package/dist/commands/remove.d.ts +3 -0
- package/dist/commands/run.d.ts +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +113 -1
- package/dist/monitor/daemon/circuit-breaker.d.ts +121 -0
- package/dist/monitor/daemon/circuit-breaker.js +552 -0
- package/dist/monitor/daemon/cli.d.ts +8 -0
- package/dist/monitor/daemon/cli.js +82 -0
- package/dist/monitor/daemon/index.d.ts +137 -0
- package/dist/monitor/daemon/index.js +695 -0
- package/dist/monitor/daemon/notifier.d.ts +25 -0
- package/dist/monitor/daemon/notifier.js +98 -0
- package/dist/monitor/daemon/processor.d.ts +89 -0
- package/dist/monitor/daemon/processor.js +455 -0
- package/dist/monitor/daemon/state.d.ts +80 -0
- package/dist/monitor/daemon/state.js +162 -0
- package/dist/monitor/daemon/watcher.d.ts +47 -0
- package/dist/monitor/daemon/watcher.js +171 -0
- package/dist/monitor/daemon/wrapper-manager.d.ts +106 -0
- package/dist/monitor/daemon/wrapper-manager.js +374 -0
- package/dist/monitor/hooks/emit-event.js +652 -0
- package/dist/monitor/persistence/file-store.d.ts +88 -0
- package/dist/monitor/persistence/file-store.js +335 -0
- package/dist/monitor/persistence/index.d.ts +7 -0
- package/dist/monitor/persistence/index.js +10 -0
- package/dist/monitor/server/adapters/redis.d.ts +38 -0
- package/dist/monitor/server/adapters/redis.js +213 -0
- package/dist/monitor/server/adapters/unix-socket.d.ts +33 -0
- package/dist/monitor/server/adapters/unix-socket.js +182 -0
- package/dist/monitor/server/broker.d.ts +135 -0
- package/dist/monitor/server/broker.js +475 -0
- package/dist/monitor/server/cli.d.ts +8 -0
- package/dist/monitor/server/cli.js +98 -0
- package/dist/monitor/server/fastify.d.ts +16 -0
- package/dist/monitor/server/fastify.js +184 -0
- package/dist/monitor/server/index.d.ts +36 -0
- package/dist/monitor/server/index.js +153 -0
- package/dist/monitor/server/websocket.d.ts +80 -0
- package/dist/monitor/server/websocket.js +453 -0
- package/dist/monitor/ui/assets/index-4IssW9On.js +59 -0
- package/dist/monitor/ui/assets/index-vo9hLe5R.css +32 -0
- package/dist/monitor/ui/favicon.png +0 -0
- package/dist/monitor/ui/index.html +14 -0
- package/dist/monitor/ui/logo.png +0 -0
- package/dist/monitor/ui/logo.svg +1 -0
- package/dist/runtime/driver.d.ts +16 -0
- package/dist/runtime/tools.d.ts +10 -0
- package/dist/templates/README.md +33 -7
- package/dist/templates/agents/aegis.md +4 -0
- package/dist/templates/agents/archon.md +13 -22
- package/dist/templates/agents/atlas.md +4 -0
- package/dist/templates/agents/canvas.md +4 -0
- package/dist/templates/agents/chronicle.md +4 -0
- package/dist/templates/agents/chronos.md +4 -0
- package/dist/templates/agents/cipher.md +4 -0
- package/dist/templates/agents/crucible.md +4 -0
- package/dist/templates/agents/echo.md +4 -0
- package/dist/templates/agents/forge.md +4 -0
- package/dist/templates/agents/ledger.md +4 -0
- package/dist/templates/agents/meridian.md +4 -0
- package/dist/templates/agents/nexus.md +4 -0
- package/dist/templates/agents/pythia.md +217 -0
- package/dist/templates/agents/scribe.md +4 -0
- package/dist/templates/agents/sentinel.md +4 -0
- package/dist/templates/agents/{oracle.md → thoth.md} +11 -7
- package/dist/templates/agents/unity.md +4 -0
- package/dist/templates/agents/vox.md +4 -0
- package/dist/templates/agents/weaver.md +4 -0
- package/dist/templates/framework-agents/documentation-expert.md +3 -3
- package/dist/templates/framework-agents/tools-expert.md +8 -8
- package/dist/templates/skills/consult.md +138 -0
- package/dist/templates/skills/orchestrate.md +173 -0
- package/dist/templates/skills/pythia.md +37 -0
- package/dist/templates/standards/agent-roles.md +68 -21
- package/dist/templates/standards/coding-standards.md +9 -26
- package/dist/templates/templates/context.md +17 -2
- package/dist/templates/templates/contextuate.md +21 -28
- package/dist/templates/templates/standards/go.md +167 -0
- package/dist/templates/templates/standards/java.md +167 -0
- package/dist/templates/templates/standards/javascript.md +292 -0
- package/dist/templates/templates/standards/php.md +181 -0
- package/dist/templates/templates/standards/python.md +175 -0
- package/dist/templates/tools/agent-creator.md +252 -0
- package/dist/templates/tools/agent-creator.tool.md +2 -2
- package/dist/templates/tools/quickref.md +216 -0
- package/dist/templates/tools/spawn.md +31 -0
- package/dist/templates/tools/standards-detector.md +301 -0
- package/dist/templates/version.json +1 -1
- package/dist/types/monitor.d.ts +660 -0
- package/dist/types/monitor.js +75 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/tokens.d.ts +10 -0
- package/package.json +18 -5
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* WebSocket Server
|
|
4
|
+
*
|
|
5
|
+
* Provides real-time event streaming to connected UI clients.
|
|
6
|
+
* Uses the 'ws' library for WebSocket handling.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.MonitorWebSocketServer = void 0;
|
|
10
|
+
const ws_1 = require("ws");
|
|
11
|
+
const uuid_1 = require("uuid");
|
|
12
|
+
class MonitorWebSocketServer {
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.wss = null;
|
|
15
|
+
this.clients = new Map();
|
|
16
|
+
this.port = options.port;
|
|
17
|
+
this.host = options.host || '0.0.0.0';
|
|
18
|
+
this.broker = options.broker;
|
|
19
|
+
this.persistence = options.persistence;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Start the WebSocket server
|
|
23
|
+
*/
|
|
24
|
+
async start() {
|
|
25
|
+
this.wss = new ws_1.WebSocketServer({
|
|
26
|
+
port: this.port,
|
|
27
|
+
host: this.host,
|
|
28
|
+
});
|
|
29
|
+
this.wss.on('connection', (ws) => this.handleConnection(ws));
|
|
30
|
+
this.wss.on('error', (err) => {
|
|
31
|
+
console.error('[WebSocket] Server error:', err);
|
|
32
|
+
});
|
|
33
|
+
// Subscribe to broker events
|
|
34
|
+
this.unsubscribeBroker = this.broker.onEvent((type, data) => {
|
|
35
|
+
this.handleBrokerEvent(type, data);
|
|
36
|
+
});
|
|
37
|
+
console.log(`[WebSocket] Server listening on ws://${this.host}:${this.port}`);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Stop the WebSocket server
|
|
41
|
+
*/
|
|
42
|
+
async stop() {
|
|
43
|
+
if (this.unsubscribeBroker) {
|
|
44
|
+
this.unsubscribeBroker();
|
|
45
|
+
}
|
|
46
|
+
if (this.wss) {
|
|
47
|
+
// Forcefully close all client connections
|
|
48
|
+
for (const ws of this.wss.clients) {
|
|
49
|
+
try {
|
|
50
|
+
ws.send(JSON.stringify({ type: 'error', message: 'Server shutting down' }));
|
|
51
|
+
ws.terminate(); // Force close the connection
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
// Ignore errors during shutdown
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
// Add timeout to force resolve if close takes too long
|
|
59
|
+
const timeout = setTimeout(() => {
|
|
60
|
+
console.log('[WebSocket] Server close timeout, forcing shutdown');
|
|
61
|
+
this.wss = null;
|
|
62
|
+
this.clients.clear();
|
|
63
|
+
resolve();
|
|
64
|
+
}, 3000);
|
|
65
|
+
this.wss.close(() => {
|
|
66
|
+
clearTimeout(timeout);
|
|
67
|
+
this.wss = null;
|
|
68
|
+
this.clients.clear();
|
|
69
|
+
console.log('[WebSocket] Server stopped');
|
|
70
|
+
resolve();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Handle new WebSocket connection
|
|
77
|
+
*/
|
|
78
|
+
handleConnection(ws) {
|
|
79
|
+
const clientId = (0, uuid_1.v4)();
|
|
80
|
+
const client = {
|
|
81
|
+
id: clientId,
|
|
82
|
+
subscription: {
|
|
83
|
+
sessionIds: new Set(),
|
|
84
|
+
allSessions: false,
|
|
85
|
+
},
|
|
86
|
+
showHidden: false,
|
|
87
|
+
send: (message) => {
|
|
88
|
+
if (ws.readyState === ws_1.WebSocket.OPEN) {
|
|
89
|
+
ws.send(JSON.stringify(message));
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
this.clients.set(clientId, client);
|
|
94
|
+
console.log(`[WebSocket] Client connected: ${clientId}`);
|
|
95
|
+
ws.on('message', (data) => {
|
|
96
|
+
try {
|
|
97
|
+
const message = JSON.parse(data.toString());
|
|
98
|
+
this.handleClientMessage(client, message);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
console.error('[WebSocket] Failed to parse message:', err);
|
|
102
|
+
client.send({ type: 'error', message: 'Invalid message format' });
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
ws.on('close', () => {
|
|
106
|
+
this.clients.delete(clientId);
|
|
107
|
+
console.log(`[WebSocket] Client disconnected: ${clientId}`);
|
|
108
|
+
});
|
|
109
|
+
ws.on('error', (err) => {
|
|
110
|
+
console.error(`[WebSocket] Client error (${clientId}):`, err);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Handle message from client
|
|
115
|
+
*/
|
|
116
|
+
async handleClientMessage(client, message) {
|
|
117
|
+
switch (message.type) {
|
|
118
|
+
case 'subscribe':
|
|
119
|
+
if (message.sessionIds && message.sessionIds.length > 0) {
|
|
120
|
+
// Subscribe to specific sessions
|
|
121
|
+
client.subscription.allSessions = false;
|
|
122
|
+
for (const id of message.sessionIds) {
|
|
123
|
+
client.subscription.sessionIds.add(id);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// Subscribe to all sessions
|
|
128
|
+
client.subscription.allSessions = true;
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
case 'unsubscribe':
|
|
132
|
+
for (const id of message.sessionIds) {
|
|
133
|
+
client.subscription.sessionIds.delete(id);
|
|
134
|
+
}
|
|
135
|
+
break;
|
|
136
|
+
case 'get_sessions':
|
|
137
|
+
const sessions = this.broker.getSessions(client.showHidden);
|
|
138
|
+
client.send({ type: 'sessions', sessions });
|
|
139
|
+
break;
|
|
140
|
+
case 'get_events':
|
|
141
|
+
await this.sendHistoricalEvents(client, message.sessionId, message.limit, message.before);
|
|
142
|
+
break;
|
|
143
|
+
case 'get_all_recent_events':
|
|
144
|
+
await this.sendAllRecentEvents(client, message.limit);
|
|
145
|
+
break;
|
|
146
|
+
case 'get_event_detail':
|
|
147
|
+
await this.sendEventDetail(client, message.sessionId, message.eventId);
|
|
148
|
+
break;
|
|
149
|
+
case 'send_input':
|
|
150
|
+
// Legacy - kept for backward compatibility
|
|
151
|
+
console.log(`[WebSocket] Input for session ${message.sessionId}: ${message.input}`);
|
|
152
|
+
break;
|
|
153
|
+
case 'inject_input':
|
|
154
|
+
// Forward input injection to daemon via broker
|
|
155
|
+
this.broker.sendToDaemon({
|
|
156
|
+
type: 'inject_input',
|
|
157
|
+
wrapperId: message.wrapperId,
|
|
158
|
+
input: message.input,
|
|
159
|
+
});
|
|
160
|
+
break;
|
|
161
|
+
case 'resize_wrapper':
|
|
162
|
+
// Forward resize to daemon via broker
|
|
163
|
+
this.broker.sendToDaemon({
|
|
164
|
+
type: 'resize_wrapper',
|
|
165
|
+
wrapperId: message.wrapperId,
|
|
166
|
+
cols: message.cols,
|
|
167
|
+
rows: message.rows,
|
|
168
|
+
});
|
|
169
|
+
break;
|
|
170
|
+
case 'get_wrappers':
|
|
171
|
+
// Request current wrapper list from daemon
|
|
172
|
+
this.broker.sendToDaemon({ type: 'get_wrappers' });
|
|
173
|
+
break;
|
|
174
|
+
case 'spawn_wrapper':
|
|
175
|
+
// Forward spawn request to daemon
|
|
176
|
+
this.broker.sendToDaemon({
|
|
177
|
+
type: 'spawn_wrapper',
|
|
178
|
+
cwd: message.cwd,
|
|
179
|
+
args: message.args,
|
|
180
|
+
cols: message.cols,
|
|
181
|
+
rows: message.rows,
|
|
182
|
+
});
|
|
183
|
+
break;
|
|
184
|
+
case 'kill_wrapper':
|
|
185
|
+
// Forward kill request to daemon
|
|
186
|
+
this.broker.sendToDaemon({
|
|
187
|
+
type: 'kill_wrapper',
|
|
188
|
+
wrapperId: message.wrapperId,
|
|
189
|
+
});
|
|
190
|
+
break;
|
|
191
|
+
case 'hide_session':
|
|
192
|
+
try {
|
|
193
|
+
await this.broker.hideSession(message.sessionId);
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
const error = err;
|
|
197
|
+
client.send({ type: 'error', message: error.message });
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
case 'unhide_session':
|
|
201
|
+
try {
|
|
202
|
+
await this.broker.unhideSession(message.sessionId);
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
const error = err;
|
|
206
|
+
client.send({ type: 'error', message: error.message });
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
case 'delete_session':
|
|
210
|
+
try {
|
|
211
|
+
await this.broker.deleteSession(message.sessionId);
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
const error = err;
|
|
215
|
+
client.send({ type: 'error', message: error.message });
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
case 'hide_all_sessions':
|
|
219
|
+
try {
|
|
220
|
+
await this.broker.hideAllSessions();
|
|
221
|
+
const updatedSessions = this.broker.getSessions(client.showHidden);
|
|
222
|
+
this.broadcastSessionsUpdate(updatedSessions);
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
const error = err;
|
|
226
|
+
client.send({ type: 'error', message: error.message });
|
|
227
|
+
}
|
|
228
|
+
break;
|
|
229
|
+
case 'delete_all_sessions':
|
|
230
|
+
try {
|
|
231
|
+
await this.broker.deleteAllSessions();
|
|
232
|
+
const updatedSessions = this.broker.getSessions(client.showHidden);
|
|
233
|
+
this.broadcastSessionsUpdate(updatedSessions);
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
const error = err;
|
|
237
|
+
client.send({ type: 'error', message: error.message });
|
|
238
|
+
}
|
|
239
|
+
break;
|
|
240
|
+
case 'set_show_hidden':
|
|
241
|
+
client.showHidden = message.showHidden;
|
|
242
|
+
const filteredSessions = this.broker.getSessions(client.showHidden);
|
|
243
|
+
client.send({ type: 'sessions', sessions: filteredSessions });
|
|
244
|
+
break;
|
|
245
|
+
case 'set_parent':
|
|
246
|
+
try {
|
|
247
|
+
await this.broker.setParentSession(message.sessionId, message.parentSessionId);
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
const error = err;
|
|
251
|
+
client.send({ type: 'error', message: error.message });
|
|
252
|
+
}
|
|
253
|
+
break;
|
|
254
|
+
case 'toggle_pin':
|
|
255
|
+
try {
|
|
256
|
+
await this.broker.togglePin(message.sessionId);
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
const error = err;
|
|
260
|
+
client.send({ type: 'error', message: error.message });
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
case 'set_user_initiated':
|
|
264
|
+
try {
|
|
265
|
+
await this.broker.setUserInitiated(message.sessionId, message.isUserInitiated);
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
const error = err;
|
|
269
|
+
client.send({ type: 'error', message: error.message });
|
|
270
|
+
}
|
|
271
|
+
break;
|
|
272
|
+
case 'rename_session':
|
|
273
|
+
try {
|
|
274
|
+
await this.broker.renameSession(message.sessionId, message.label);
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
const error = err;
|
|
278
|
+
client.send({ type: 'error', message: error.message });
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Send historical events to a client
|
|
285
|
+
*/
|
|
286
|
+
async sendHistoricalEvents(client, sessionId, limit, before) {
|
|
287
|
+
if (!this.persistence) {
|
|
288
|
+
client.send({ type: 'error', message: 'Persistence not available' });
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
const events = await this.persistence.getEvents(sessionId, {
|
|
293
|
+
limit: limit || 100,
|
|
294
|
+
before,
|
|
295
|
+
});
|
|
296
|
+
client.send({ type: 'events', sessionId, events });
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
console.error('[WebSocket] Failed to fetch events:', err);
|
|
300
|
+
client.send({ type: 'error', message: 'Failed to fetch events' });
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Send all recent events across all sessions to a client
|
|
305
|
+
*/
|
|
306
|
+
async sendAllRecentEvents(client, limit) {
|
|
307
|
+
if (!this.persistence) {
|
|
308
|
+
client.send({ type: 'error', message: 'Persistence not available' });
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
const events = await this.persistence.getAllRecentEvents(limit || 200);
|
|
313
|
+
client.send({ type: 'all_events', events });
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
console.error('[WebSocket] Failed to fetch all events:', err);
|
|
317
|
+
client.send({ type: 'error', message: 'Failed to fetch events' });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Send event detail to a client
|
|
322
|
+
*/
|
|
323
|
+
async sendEventDetail(client, sessionId, eventId) {
|
|
324
|
+
if (!this.persistence) {
|
|
325
|
+
client.send({ type: 'error', message: 'Persistence not available' });
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
const event = await this.persistence.getEventById(sessionId, eventId);
|
|
330
|
+
if (event) {
|
|
331
|
+
client.send({ type: 'event_detail', event });
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
client.send({ type: 'error', message: 'Event not found' });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch (err) {
|
|
338
|
+
console.error('[WebSocket] Failed to fetch event detail:', err);
|
|
339
|
+
client.send({ type: 'error', message: 'Failed to fetch event detail' });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Handle events from the broker
|
|
344
|
+
*/
|
|
345
|
+
handleBrokerEvent(type, data) {
|
|
346
|
+
switch (type) {
|
|
347
|
+
case 'event':
|
|
348
|
+
this.broadcastEvent(data);
|
|
349
|
+
break;
|
|
350
|
+
case 'session_created':
|
|
351
|
+
case 'session_updated':
|
|
352
|
+
case 'session_ended':
|
|
353
|
+
this.broadcastSessionUpdate(data);
|
|
354
|
+
break;
|
|
355
|
+
case 'wrapper_connected':
|
|
356
|
+
this.broadcastToAll({
|
|
357
|
+
type: 'wrapper_connected',
|
|
358
|
+
wrapperId: data.wrapperId,
|
|
359
|
+
state: data.state,
|
|
360
|
+
});
|
|
361
|
+
break;
|
|
362
|
+
case 'wrapper_disconnected':
|
|
363
|
+
this.broadcastToAll({
|
|
364
|
+
type: 'wrapper_disconnected',
|
|
365
|
+
wrapperId: data.wrapperId,
|
|
366
|
+
exitCode: data.exitCode,
|
|
367
|
+
});
|
|
368
|
+
break;
|
|
369
|
+
case 'wrapper_state':
|
|
370
|
+
this.broadcastToAll({
|
|
371
|
+
type: 'wrapper_state',
|
|
372
|
+
wrapperId: data.wrapperId,
|
|
373
|
+
state: data.state,
|
|
374
|
+
claudeSessionId: data.claudeSessionId,
|
|
375
|
+
});
|
|
376
|
+
break;
|
|
377
|
+
case 'wrapper_output':
|
|
378
|
+
this.broadcastToAll({
|
|
379
|
+
type: 'wrapper_output',
|
|
380
|
+
wrapperId: data.wrapperId,
|
|
381
|
+
data: data.data,
|
|
382
|
+
timestamp: data.timestamp,
|
|
383
|
+
});
|
|
384
|
+
break;
|
|
385
|
+
case 'wrapper_spawned':
|
|
386
|
+
this.broadcastToAll({
|
|
387
|
+
type: 'wrapper_spawned',
|
|
388
|
+
wrapperId: data.wrapperId,
|
|
389
|
+
success: data.success ?? false,
|
|
390
|
+
error: data.error,
|
|
391
|
+
});
|
|
392
|
+
break;
|
|
393
|
+
case 'wrappers_list':
|
|
394
|
+
this.broadcastToAll({
|
|
395
|
+
type: 'wrappers',
|
|
396
|
+
wrappers: data.wrappers || [],
|
|
397
|
+
});
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Broadcast a message to all connected clients
|
|
403
|
+
*/
|
|
404
|
+
broadcastToAll(message) {
|
|
405
|
+
for (const [, client] of this.clients) {
|
|
406
|
+
client.send(message);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Broadcast an event to subscribed clients
|
|
411
|
+
*/
|
|
412
|
+
broadcastEvent(event) {
|
|
413
|
+
for (const [, client] of this.clients) {
|
|
414
|
+
if (this.isClientSubscribed(client, event.sessionId)) {
|
|
415
|
+
client.send({ type: 'event', event });
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Broadcast a session update to all clients
|
|
421
|
+
*/
|
|
422
|
+
broadcastSessionUpdate(session) {
|
|
423
|
+
const message = { type: 'session_update', session };
|
|
424
|
+
for (const [, client] of this.clients) {
|
|
425
|
+
// Session updates go to all clients
|
|
426
|
+
client.send(message);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Broadcast updated session list to all clients (for bulk operations)
|
|
431
|
+
*/
|
|
432
|
+
broadcastSessionsUpdate(sessions) {
|
|
433
|
+
const message = { type: 'sessions_updated', sessions };
|
|
434
|
+
for (const [, client] of this.clients) {
|
|
435
|
+
// Filter sessions based on client's showHidden preference
|
|
436
|
+
const filteredSessions = sessions.filter(s => client.showHidden || !s.hidden);
|
|
437
|
+
client.send({ type: 'sessions_updated', sessions: filteredSessions });
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Check if a client is subscribed to events for a session
|
|
442
|
+
*/
|
|
443
|
+
isClientSubscribed(client, sessionId) {
|
|
444
|
+
return client.subscription.allSessions || client.subscription.sessionIds.has(sessionId);
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Get the number of connected clients
|
|
448
|
+
*/
|
|
449
|
+
getClientCount() {
|
|
450
|
+
return this.clients.size;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
exports.MonitorWebSocketServer = MonitorWebSocketServer;
|