cntx-ui 3.0.7 → 3.0.9
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/bin/cntx-ui.js +70 -0
- package/dist/lib/agent-runtime.js +269 -0
- package/dist/lib/agent-tools.js +162 -0
- package/dist/lib/api-router.js +387 -0
- package/dist/lib/bundle-manager.js +236 -0
- package/dist/lib/configuration-manager.js +230 -0
- package/dist/lib/database-manager.js +277 -0
- package/dist/lib/file-system-manager.js +305 -0
- package/dist/lib/function-level-chunker.js +144 -0
- package/dist/lib/heuristics-manager.js +491 -0
- package/dist/lib/mcp-server.js +159 -0
- package/dist/lib/mcp-transport.js +10 -0
- package/dist/lib/semantic-splitter.js +335 -0
- package/dist/lib/simple-vector-store.js +98 -0
- package/dist/lib/treesitter-semantic-chunker.js +277 -0
- package/dist/lib/websocket-manager.js +268 -0
- package/dist/server.js +225 -0
- package/package.json +18 -8
- package/bin/cntx-ui-mcp.sh +0 -3
- package/bin/cntx-ui.js +0 -123
- package/lib/agent-runtime.js +0 -371
- package/lib/agent-tools.js +0 -370
- package/lib/api-router.js +0 -1026
- package/lib/bundle-manager.js +0 -326
- package/lib/configuration-manager.js +0 -760
- package/lib/database-manager.js +0 -397
- package/lib/file-system-manager.js +0 -489
- package/lib/function-level-chunker.js +0 -406
- package/lib/heuristics-manager.js +0 -529
- package/lib/mcp-server.js +0 -1380
- package/lib/mcp-transport.js +0 -97
- package/lib/semantic-splitter.js +0 -304
- package/lib/simple-vector-store.js +0 -108
- package/lib/treesitter-semantic-chunker.js +0 -1485
- package/lib/websocket-manager.js +0 -470
- package/server.js +0 -687
package/lib/websocket-manager.js
DELETED
|
@@ -1,470 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WebSocket Manager for cntx-ui
|
|
3
|
-
* Handles real-time client communication and updates
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { WebSocketServer } from 'ws';
|
|
7
|
-
|
|
8
|
-
export default class WebSocketManager {
|
|
9
|
-
constructor(bundleManager, configManager, options = {}) {
|
|
10
|
-
this.bundleManager = bundleManager;
|
|
11
|
-
this.configManager = configManager;
|
|
12
|
-
this.verbose = options.verbose || false;
|
|
13
|
-
this.clients = new Set();
|
|
14
|
-
this.wss = null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// === WebSocket Server Setup ===
|
|
18
|
-
|
|
19
|
-
initialize(httpServer) {
|
|
20
|
-
this.wss = new WebSocketServer({ server: httpServer });
|
|
21
|
-
|
|
22
|
-
this.wss.on('connection', (ws) => {
|
|
23
|
-
this.handleConnection(ws);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
if (this.verbose) {
|
|
27
|
-
console.log('🔌 WebSocket server initialized');
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
handleConnection(ws) {
|
|
32
|
-
// Add client to our set
|
|
33
|
-
this.clients.add(ws);
|
|
34
|
-
if (this.verbose) {
|
|
35
|
-
console.log(`📱 WebSocket client connected (${this.clients.size} total clients)`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Send initial update to the new client
|
|
39
|
-
this.sendUpdate(ws);
|
|
40
|
-
|
|
41
|
-
// Handle client disconnect
|
|
42
|
-
ws.on('close', () => {
|
|
43
|
-
this.clients.delete(ws);
|
|
44
|
-
if (this.verbose) {
|
|
45
|
-
console.log(`📱 WebSocket client disconnected (${this.clients.size} total clients)`);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// Handle client errors
|
|
50
|
-
ws.on('error', (error) => {
|
|
51
|
-
if (this.verbose) {
|
|
52
|
-
console.error('WebSocket client error:', error.message);
|
|
53
|
-
}
|
|
54
|
-
this.clients.delete(ws);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// Optional: Handle incoming messages from clients
|
|
58
|
-
ws.on('message', (message) => {
|
|
59
|
-
try {
|
|
60
|
-
const data = JSON.parse(message.toString());
|
|
61
|
-
this.handleClientMessage(ws, data);
|
|
62
|
-
} catch (error) {
|
|
63
|
-
if (this.verbose) {
|
|
64
|
-
console.error('Invalid WebSocket message:', error.message);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
handleClientMessage(ws, data) {
|
|
71
|
-
// Handle different types of client messages
|
|
72
|
-
switch (data.type) {
|
|
73
|
-
case 'ping':
|
|
74
|
-
ws.send(JSON.stringify({ type: 'pong', timestamp: Date.now() }));
|
|
75
|
-
break;
|
|
76
|
-
|
|
77
|
-
case 'request-update':
|
|
78
|
-
this.sendUpdate(ws);
|
|
79
|
-
break;
|
|
80
|
-
|
|
81
|
-
case 'subscribe':
|
|
82
|
-
// Future: Handle subscription to specific bundle updates
|
|
83
|
-
ws.subscriptions = data.bundles || [];
|
|
84
|
-
break;
|
|
85
|
-
|
|
86
|
-
default:
|
|
87
|
-
if (this.verbose) {
|
|
88
|
-
console.warn('Unknown WebSocket message type:', data.type);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// === Client Management ===
|
|
94
|
-
|
|
95
|
-
getClientCount() {
|
|
96
|
-
return this.clients.size;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
getActiveClients() {
|
|
100
|
-
// Filter out clients that might be in a closed state
|
|
101
|
-
const activeClients = new Set();
|
|
102
|
-
|
|
103
|
-
this.clients.forEach(client => {
|
|
104
|
-
if (client.readyState === 1) { // WebSocket.OPEN
|
|
105
|
-
activeClients.add(client);
|
|
106
|
-
} else {
|
|
107
|
-
// Remove dead connections
|
|
108
|
-
this.clients.delete(client);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
return activeClients;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// === Broadcasting Updates ===
|
|
116
|
-
|
|
117
|
-
broadcastUpdate() {
|
|
118
|
-
const activeClients = this.getActiveClients();
|
|
119
|
-
|
|
120
|
-
if (activeClients.size === 0) {
|
|
121
|
-
return; // No clients to update
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (this.verbose) {
|
|
125
|
-
console.log(`📡 Broadcasting update to ${activeClients.size} client(s)`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
activeClients.forEach(client => {
|
|
129
|
-
this.sendUpdate(client);
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
sendUpdate(client) {
|
|
134
|
-
if (client.readyState !== 1) { // Not WebSocket.OPEN
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
const updateData = this.prepareUpdateData();
|
|
140
|
-
client.send(JSON.stringify(updateData));
|
|
141
|
-
} catch (error) {
|
|
142
|
-
if (this.verbose) {
|
|
143
|
-
console.error('Failed to send WebSocket update:', error.message);
|
|
144
|
-
}
|
|
145
|
-
this.clients.delete(client);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
prepareUpdateData() {
|
|
150
|
-
const bundles = this.configManager.getBundles();
|
|
151
|
-
|
|
152
|
-
const bundleData = Array.from(bundles.entries()).map(([name, bundle]) => ({
|
|
153
|
-
name,
|
|
154
|
-
changed: bundle.changed,
|
|
155
|
-
fileCount: bundle.files.length,
|
|
156
|
-
content: bundle.content.substring(0, 2000) + (bundle.content.length > 2000 ? '...' : ''),
|
|
157
|
-
files: bundle.files,
|
|
158
|
-
lastGenerated: bundle.generated,
|
|
159
|
-
size: bundle.size,
|
|
160
|
-
patterns: bundle.patterns
|
|
161
|
-
}));
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
type: 'bundle-update',
|
|
165
|
-
timestamp: new Date().toISOString(),
|
|
166
|
-
bundles: bundleData,
|
|
167
|
-
serverStatus: {
|
|
168
|
-
uptime: process.uptime(),
|
|
169
|
-
scanning: this.bundleManager._isScanning || false,
|
|
170
|
-
totalFiles: this.bundleManager.fileSystemManager?.getAllFiles()?.length || 0
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// === Targeted Updates ===
|
|
176
|
-
|
|
177
|
-
broadcastBundleUpdate(bundleName) {
|
|
178
|
-
const activeClients = this.getActiveClients();
|
|
179
|
-
|
|
180
|
-
if (activeClients.size === 0) {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (this.verbose) {
|
|
185
|
-
console.log(`📡 Broadcasting ${bundleName} bundle update to ${activeClients.size} client(s)`);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const bundle = this.configManager.getBundles().get(bundleName);
|
|
189
|
-
if (!bundle) {
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const updateData = {
|
|
194
|
-
type: 'bundle-specific-update',
|
|
195
|
-
timestamp: new Date().toISOString(),
|
|
196
|
-
bundleName,
|
|
197
|
-
bundle: {
|
|
198
|
-
name: bundleName,
|
|
199
|
-
changed: bundle.changed,
|
|
200
|
-
fileCount: bundle.files.length,
|
|
201
|
-
content: bundle.content.substring(0, 2000) + (bundle.content.length > 2000 ? '...' : ''),
|
|
202
|
-
files: bundle.files,
|
|
203
|
-
lastGenerated: bundle.generated,
|
|
204
|
-
size: bundle.size,
|
|
205
|
-
patterns: bundle.patterns
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
activeClients.forEach(client => {
|
|
210
|
-
try {
|
|
211
|
-
if (client.readyState === 1) {
|
|
212
|
-
client.send(JSON.stringify(updateData));
|
|
213
|
-
}
|
|
214
|
-
} catch (error) {
|
|
215
|
-
if (this.verbose) {
|
|
216
|
-
console.error('Failed to send bundle update:', error.message);
|
|
217
|
-
}
|
|
218
|
-
this.clients.delete(client);
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
broadcastFileChange(filename, eventType) {
|
|
224
|
-
const activeClients = this.getActiveClients();
|
|
225
|
-
|
|
226
|
-
if (activeClients.size === 0) {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const updateData = {
|
|
231
|
-
type: 'file-change',
|
|
232
|
-
timestamp: new Date().toISOString(),
|
|
233
|
-
filename,
|
|
234
|
-
eventType,
|
|
235
|
-
message: `File ${eventType}: ${filename}`
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
activeClients.forEach(client => {
|
|
239
|
-
try {
|
|
240
|
-
if (client.readyState === 1) {
|
|
241
|
-
client.send(JSON.stringify(updateData));
|
|
242
|
-
}
|
|
243
|
-
} catch (error) {
|
|
244
|
-
if (this.verbose) {
|
|
245
|
-
console.error('Failed to send file change update:', error.message);
|
|
246
|
-
}
|
|
247
|
-
this.clients.delete(client);
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
broadcastStatusUpdate(status) {
|
|
253
|
-
const activeClients = this.getActiveClients();
|
|
254
|
-
|
|
255
|
-
if (activeClients.size === 0) {
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const updateData = {
|
|
260
|
-
type: 'status-update',
|
|
261
|
-
timestamp: new Date().toISOString(),
|
|
262
|
-
status
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
activeClients.forEach(client => {
|
|
266
|
-
try {
|
|
267
|
-
if (client.readyState === 1) {
|
|
268
|
-
client.send(JSON.stringify(updateData));
|
|
269
|
-
}
|
|
270
|
-
} catch (error) {
|
|
271
|
-
if (this.verbose) {
|
|
272
|
-
console.error('Failed to send status update:', error.message);
|
|
273
|
-
}
|
|
274
|
-
this.clients.delete(client);
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// === Utility Methods ===
|
|
280
|
-
|
|
281
|
-
ping() {
|
|
282
|
-
const activeClients = this.getActiveClients();
|
|
283
|
-
|
|
284
|
-
const pingData = {
|
|
285
|
-
type: 'ping',
|
|
286
|
-
timestamp: new Date().toISOString(),
|
|
287
|
-
serverTime: Date.now()
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
activeClients.forEach(client => {
|
|
291
|
-
try {
|
|
292
|
-
if (client.readyState === 1) {
|
|
293
|
-
client.send(JSON.stringify(pingData));
|
|
294
|
-
}
|
|
295
|
-
} catch (error) {
|
|
296
|
-
this.clients.delete(client);
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// === Cleanup ===
|
|
302
|
-
|
|
303
|
-
close() {
|
|
304
|
-
if (this.wss) {
|
|
305
|
-
if (this.verbose) {
|
|
306
|
-
console.log('🔌 Closing WebSocket server...');
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Close all client connections
|
|
310
|
-
this.clients.forEach(client => {
|
|
311
|
-
try {
|
|
312
|
-
if (client.readyState === 1) {
|
|
313
|
-
client.close(1000, 'Server shutting down');
|
|
314
|
-
}
|
|
315
|
-
} catch (error) {
|
|
316
|
-
if (this.verbose) {
|
|
317
|
-
console.error('Error closing WebSocket client:', error.message);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
// Close the WebSocket server
|
|
323
|
-
this.wss.close(() => {
|
|
324
|
-
if (this.verbose) {
|
|
325
|
-
console.log('🔌 WebSocket server closed');
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
this.clients.clear();
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// === Health Check ===
|
|
334
|
-
|
|
335
|
-
getHealthStatus() {
|
|
336
|
-
return {
|
|
337
|
-
connected: this.clients.size,
|
|
338
|
-
active: this.getActiveClients().size,
|
|
339
|
-
server: this.wss ? 'running' : 'stopped'
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// === Event Handlers for Integration ===
|
|
344
|
-
|
|
345
|
-
onBundleGenerated(bundleName) {
|
|
346
|
-
this.broadcastBundleUpdate(bundleName);
|
|
347
|
-
this.broadcastBundleSyncCompleted(bundleName);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
onBundlesGenerated() {
|
|
351
|
-
this.broadcastUpdate();
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
onConfigChanged() {
|
|
355
|
-
this.broadcastUpdate();
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
onFileChanged(filename, eventType) {
|
|
359
|
-
// Notify which bundles are affected by this file change
|
|
360
|
-
const affectedBundles = this.getAffectedBundles(filename);
|
|
361
|
-
|
|
362
|
-
affectedBundles.forEach(bundleName => {
|
|
363
|
-
this.broadcastBundleFileChanged(bundleName, filename);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
this.broadcastFileChange(filename, eventType);
|
|
367
|
-
|
|
368
|
-
// Also broadcast bundle updates after a short delay to allow bundle regeneration
|
|
369
|
-
setTimeout(() => {
|
|
370
|
-
this.broadcastUpdate();
|
|
371
|
-
}, 500);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// === New Bundle Sync Event Handlers ===
|
|
375
|
-
|
|
376
|
-
onBundleSyncStarted(bundleName) {
|
|
377
|
-
const updateData = {
|
|
378
|
-
type: 'bundle-sync-started',
|
|
379
|
-
bundleName,
|
|
380
|
-
timestamp: new Date().toISOString()
|
|
381
|
-
};
|
|
382
|
-
this.broadcastToActiveClients(updateData);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
onBundleSyncCompleted(bundleName) {
|
|
386
|
-
const updateData = {
|
|
387
|
-
type: 'bundle-sync-completed',
|
|
388
|
-
bundleName,
|
|
389
|
-
timestamp: new Date().toISOString()
|
|
390
|
-
};
|
|
391
|
-
this.broadcastToActiveClients(updateData);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
onBundleSyncFailed(bundleName, error) {
|
|
395
|
-
const updateData = {
|
|
396
|
-
type: 'bundle-sync-failed',
|
|
397
|
-
bundleName,
|
|
398
|
-
error: error.message || error,
|
|
399
|
-
timestamp: new Date().toISOString()
|
|
400
|
-
};
|
|
401
|
-
this.broadcastToActiveClients(updateData);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
broadcastBundleFileChanged(bundleName, filename) {
|
|
405
|
-
const updateData = {
|
|
406
|
-
type: 'bundle-file-changed',
|
|
407
|
-
bundleName,
|
|
408
|
-
filename,
|
|
409
|
-
timestamp: new Date().toISOString()
|
|
410
|
-
};
|
|
411
|
-
this.broadcastToActiveClients(updateData);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
broadcastBundleSyncCompleted(bundleName) {
|
|
415
|
-
const updateData = {
|
|
416
|
-
type: 'bundle-sync-completed',
|
|
417
|
-
bundleName,
|
|
418
|
-
timestamp: new Date().toISOString()
|
|
419
|
-
};
|
|
420
|
-
this.broadcastToActiveClients(updateData);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Helper method to broadcast to all active clients
|
|
424
|
-
broadcastToActiveClients(data) {
|
|
425
|
-
const activeClients = this.getActiveClients();
|
|
426
|
-
|
|
427
|
-
if (activeClients.size === 0) {
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
activeClients.forEach(client => {
|
|
432
|
-
try {
|
|
433
|
-
if (client.readyState === 1) {
|
|
434
|
-
client.send(JSON.stringify(data));
|
|
435
|
-
}
|
|
436
|
-
} catch (error) {
|
|
437
|
-
if (this.verbose) {
|
|
438
|
-
console.error('Failed to send WebSocket update:', error.message);
|
|
439
|
-
}
|
|
440
|
-
this.clients.delete(client);
|
|
441
|
-
}
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// Helper to find which bundles are affected by a file change
|
|
446
|
-
getAffectedBundles(filename) {
|
|
447
|
-
const bundles = this.configManager.getBundles();
|
|
448
|
-
const affectedBundles = [];
|
|
449
|
-
|
|
450
|
-
bundles.forEach((bundle, name) => {
|
|
451
|
-
const matchesBundle = bundle.patterns.some(pattern =>
|
|
452
|
-
this.bundleManager.fileSystemManager.matchesPattern(filename, pattern)
|
|
453
|
-
);
|
|
454
|
-
|
|
455
|
-
if (matchesBundle) {
|
|
456
|
-
affectedBundles.push(name);
|
|
457
|
-
}
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
return affectedBundles;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
onHiddenFilesChanged() {
|
|
464
|
-
this.broadcastUpdate();
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
onIgnorePatternsChanged() {
|
|
468
|
-
this.broadcastUpdate();
|
|
469
|
-
}
|
|
470
|
-
}
|