cntx-ui 2.0.15 → 3.0.1
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 +40 -344
- package/bin/cntx-ui-mcp.sh +3 -0
- package/bin/cntx-ui.js +2 -1
- package/lib/agent-runtime.js +161 -1340
- package/lib/agent-tools.js +9 -7
- package/lib/api-router.js +262 -79
- package/lib/bundle-manager.js +172 -407
- package/lib/configuration-manager.js +94 -59
- package/lib/database-manager.js +397 -0
- package/lib/file-system-manager.js +17 -0
- package/lib/heuristics-manager.js +119 -17
- package/lib/mcp-server.js +125 -55
- package/lib/semantic-splitter.js +222 -481
- package/lib/simple-vector-store.js +69 -300
- package/package.json +18 -31
- package/server.js +151 -73
- package/templates/TOOLS.md +41 -0
- package/templates/activities/activities/create-project-bundles/README.md +4 -3
- package/templates/activities/activities/create-project-bundles/notes.md +15 -19
- package/templates/activities/activities/create-project-bundles/tasks.md +4 -4
- package/templates/activities/activities.json +1 -1
- package/templates/agent-config.yaml +0 -13
- package/templates/agent-instructions.md +22 -6
- package/templates/agent-rules/capabilities/bundle-system.md +1 -1
- package/templates/agent-rules/project-specific/architecture.md +1 -1
- package/web/dist/assets/index-B2OdTzzI.css +1 -0
- package/web/dist/assets/index-D0tBsKiR.js +2016 -0
- package/web/dist/index.html +2 -2
- package/mcp-config-example.json +0 -9
- package/web/dist/assets/heuristics-manager-browser-DfonOP5I.js +0 -1
- package/web/dist/assets/index-dF3qg-y_.js +0 -2486
- package/web/dist/assets/index-h5FGSg_P.css +0 -1
package/lib/agent-tools.js
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
* Built on top of existing cntx-ui infrastructure
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { readFileSync, existsSync } from 'fs';
|
|
7
|
-
import { join, relative } from 'path';
|
|
6
|
+
import { readFileSync, existsSync, statSync } from 'fs';
|
|
7
|
+
import { join, relative, extname } from 'path';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
8
10
|
import { exec } from 'child_process';
|
|
9
11
|
import { promisify } from 'util';
|
|
10
12
|
|
|
@@ -106,8 +108,8 @@ export class AgentTools {
|
|
|
106
108
|
// Apply semantic search if vector store is available
|
|
107
109
|
if (this.cntxServer.vectorStoreInitialized) {
|
|
108
110
|
try {
|
|
109
|
-
const searchResults = await this.cntxServer.vectorStore.search(query, maxResults * 2);
|
|
110
|
-
const chunkIds = searchResults.map(r => r.
|
|
111
|
+
const searchResults = await this.cntxServer.vectorStore.search(query, { limit: maxResults * 2 });
|
|
112
|
+
const chunkIds = searchResults.map(r => r.id || r.chunkId).filter(Boolean);
|
|
111
113
|
chunks = chunks.filter(c => chunkIds.includes(c.id));
|
|
112
114
|
} catch (error) {
|
|
113
115
|
// Fall back to text-based search
|
|
@@ -318,7 +320,7 @@ export class AgentTools {
|
|
|
318
320
|
getFileSize(filePath) {
|
|
319
321
|
try {
|
|
320
322
|
const fullPath = join(this.cntxServer.CWD, filePath);
|
|
321
|
-
const stats =
|
|
323
|
+
const stats = statSync(fullPath);
|
|
322
324
|
return stats.size;
|
|
323
325
|
} catch {
|
|
324
326
|
return 0;
|
|
@@ -326,7 +328,7 @@ export class AgentTools {
|
|
|
326
328
|
}
|
|
327
329
|
|
|
328
330
|
getFileType(filePath) {
|
|
329
|
-
const ext =
|
|
331
|
+
const ext = extname(filePath).toLowerCase();
|
|
330
332
|
const typeMap = {
|
|
331
333
|
'.js': 'javascript',
|
|
332
334
|
'.jsx': 'javascript',
|
|
@@ -345,7 +347,7 @@ export class AgentTools {
|
|
|
345
347
|
}
|
|
346
348
|
|
|
347
349
|
getMimeType(filePath) {
|
|
348
|
-
const ext =
|
|
350
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
349
351
|
const mimeTypes = {
|
|
350
352
|
'.js': 'application/javascript',
|
|
351
353
|
'.jsx': 'application/javascript',
|
package/lib/api-router.js
CHANGED
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { parse } from 'url';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
7
9
|
|
|
8
10
|
export default class APIRouter {
|
|
9
|
-
constructor(configManager, bundleManager, fileSystemManager, semanticAnalysisManager, vectorStore, activityManager) {
|
|
11
|
+
constructor(cntxServer, configManager, bundleManager, fileSystemManager, semanticAnalysisManager, vectorStore, activityManager) {
|
|
12
|
+
this.cntxServer = cntxServer;
|
|
10
13
|
this.configManager = configManager;
|
|
11
14
|
this.bundleManager = bundleManager;
|
|
12
15
|
this.fileSystemManager = fileSystemManager;
|
|
@@ -19,6 +22,12 @@ export default class APIRouter {
|
|
|
19
22
|
const method = req.method;
|
|
20
23
|
const pathname = url.pathname;
|
|
21
24
|
|
|
25
|
+
// DEBUG: Log all incoming API requests
|
|
26
|
+
console.log('[API REQUEST]', method, pathname);
|
|
27
|
+
if (pathname.includes('database')) {
|
|
28
|
+
console.log('[DATABASE] Route requested:', pathname, method);
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
try {
|
|
23
32
|
// Route to appropriate handler
|
|
24
33
|
if (pathname === '/api/bundles' && method === 'GET') {
|
|
@@ -164,6 +173,15 @@ export default class APIRouter {
|
|
|
164
173
|
return await this.handlePostVectorDbSearch(req, res);
|
|
165
174
|
}
|
|
166
175
|
|
|
176
|
+
if (pathname === '/api/vector-db/network' && method === 'GET') {
|
|
177
|
+
return await this.handleGetVectorDbNetwork(req, res);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (pathname === '/api/semantic-search' && method === 'POST') {
|
|
181
|
+
return await this.handlePostSemanticSearch(req, res);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
167
185
|
if (pathname === '/api/vector-db/search-by-type' && method === 'POST') {
|
|
168
186
|
return await this.handlePostVectorDbSearchByType(req, res);
|
|
169
187
|
}
|
|
@@ -176,6 +194,11 @@ export default class APIRouter {
|
|
|
176
194
|
return await this.handleGetActivities(req, res);
|
|
177
195
|
}
|
|
178
196
|
|
|
197
|
+
if (pathname.startsWith('/api/activities/') && pathname.endsWith('/reasoning') && method === 'GET') {
|
|
198
|
+
const activityId = pathname.split('/')[3];
|
|
199
|
+
return await this.handleGetActivityReasoning(req, res, activityId);
|
|
200
|
+
}
|
|
201
|
+
|
|
179
202
|
if (pathname.startsWith('/api/activities/') && pathname.endsWith('/execute') && method === 'POST') {
|
|
180
203
|
const activityId = pathname.split('/')[3];
|
|
181
204
|
return await this.handlePostActivityExecute(req, res, activityId);
|
|
@@ -190,6 +213,22 @@ export default class APIRouter {
|
|
|
190
213
|
return await this.handleOpenFile(req, res);
|
|
191
214
|
}
|
|
192
215
|
|
|
216
|
+
if (pathname === '/api/bundle-sync-status' && method === 'GET') {
|
|
217
|
+
return await this.handleGetBundleSyncStatus(req, res);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (pathname === '/api/bundle-sync-status' && method === 'POST') {
|
|
221
|
+
return await this.handlePostBundleSyncStatus(req, res);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (pathname === '/api/database/info' && method === 'GET') {
|
|
225
|
+
return await this.handleGetDatabaseInfo(req, res);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (pathname === '/api/database/query' && method === 'POST') {
|
|
229
|
+
return await this.handlePostDatabaseQuery(req, res);
|
|
230
|
+
}
|
|
231
|
+
|
|
193
232
|
// If no route matches, return 404
|
|
194
233
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
195
234
|
res.end(JSON.stringify({ error: 'API endpoint not found' }));
|
|
@@ -204,20 +243,13 @@ export default class APIRouter {
|
|
|
204
243
|
// === Bundle Operations ===
|
|
205
244
|
|
|
206
245
|
async handleGetBundles(req, res, url) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
patterns: bundle.patterns,
|
|
215
|
-
size: bundle.size,
|
|
216
|
-
generated: bundle.generated
|
|
217
|
-
}));
|
|
218
|
-
|
|
219
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
220
|
-
res.end(JSON.stringify(bundleData));
|
|
246
|
+
try {
|
|
247
|
+
const bundleInfo = this.bundleManager.getAllBundleInfo();
|
|
248
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
249
|
+
res.end(JSON.stringify(bundleInfo));
|
|
250
|
+
} catch (error) {
|
|
251
|
+
this.sendError(res, 500, error.message);
|
|
252
|
+
}
|
|
221
253
|
}
|
|
222
254
|
|
|
223
255
|
async handlePostBundles(req, res) {
|
|
@@ -243,7 +275,7 @@ export default class APIRouter {
|
|
|
243
275
|
}
|
|
244
276
|
// Ensure we're working with relative paths
|
|
245
277
|
const relativeAddFileName = fileName.startsWith('/') ?
|
|
246
|
-
|
|
278
|
+
path.relative(this.configManager.CWD, fileName) : fileName;
|
|
247
279
|
if (!bundle.files.includes(relativeAddFileName)) {
|
|
248
280
|
bundle.files.push(relativeAddFileName);
|
|
249
281
|
bundle.changed = true;
|
|
@@ -257,7 +289,7 @@ export default class APIRouter {
|
|
|
257
289
|
}
|
|
258
290
|
// Ensure we're working with relative paths for both search and removal
|
|
259
291
|
const relativeRemoveFileName = fileName.startsWith('/') ?
|
|
260
|
-
|
|
292
|
+
path.relative(this.configManager.CWD, fileName) : fileName;
|
|
261
293
|
const removeIndex = bundle.files.indexOf(relativeRemoveFileName);
|
|
262
294
|
if (removeIndex > -1) {
|
|
263
295
|
bundle.files.splice(removeIndex, 1);
|
|
@@ -273,7 +305,7 @@ export default class APIRouter {
|
|
|
273
305
|
fileNames.forEach(file => {
|
|
274
306
|
// Ensure we're working with relative paths
|
|
275
307
|
const relativeFile = file.startsWith('/') ?
|
|
276
|
-
|
|
308
|
+
path.relative(this.configManager.CWD, file) : file;
|
|
277
309
|
if (!bundle.files.includes(relativeFile)) {
|
|
278
310
|
bundle.files.push(relativeFile);
|
|
279
311
|
}
|
|
@@ -289,7 +321,7 @@ export default class APIRouter {
|
|
|
289
321
|
fileNames.forEach(file => {
|
|
290
322
|
// Ensure we're working with relative paths
|
|
291
323
|
const relativeFile = file.startsWith('/') ?
|
|
292
|
-
|
|
324
|
+
path.relative(this.configManager.CWD, file) : file;
|
|
293
325
|
const index = bundle.files.indexOf(relativeFile);
|
|
294
326
|
if (index > -1) {
|
|
295
327
|
bundle.files.splice(index, 1);
|
|
@@ -378,9 +410,8 @@ export default class APIRouter {
|
|
|
378
410
|
editor: this.configManager.getEditor()
|
|
379
411
|
};
|
|
380
412
|
|
|
381
|
-
bundles
|
|
382
|
-
|
|
383
|
-
});
|
|
413
|
+
// Note: bundles are now managed separately in bundle-states.json
|
|
414
|
+
// config.json only contains non-bundle settings
|
|
384
415
|
|
|
385
416
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
386
417
|
res.end(JSON.stringify(config));
|
|
@@ -570,12 +601,14 @@ export default class APIRouter {
|
|
|
570
601
|
const { content } = JSON.parse(body);
|
|
571
602
|
|
|
572
603
|
// Save content and reload patterns
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
604
|
+
const success = this.configManager.saveCntxignore(content);
|
|
605
|
+
if (!success) {
|
|
606
|
+
return this.sendError(res, 500, 'Failed to save .cntxignore');
|
|
607
|
+
}
|
|
576
608
|
|
|
577
|
-
|
|
578
|
-
|
|
609
|
+
this.fileSystemManager.setIgnorePatterns(this.configManager.ignorePatterns);
|
|
610
|
+
|
|
611
|
+
this.sendResponse(res, 200, { success: true });
|
|
579
612
|
}
|
|
580
613
|
|
|
581
614
|
async handleGetGitignore(req, res) {
|
|
@@ -624,37 +657,20 @@ export default class APIRouter {
|
|
|
624
657
|
name: chunk.name,
|
|
625
658
|
code: chunk.code,
|
|
626
659
|
semanticType: chunk.subtype || chunk.type || 'unknown',
|
|
627
|
-
businessDomain: chunk.
|
|
628
|
-
technicalPatterns: chunk.
|
|
660
|
+
businessDomain: chunk.businessDomain || [],
|
|
661
|
+
technicalPatterns: chunk.technicalPatterns || [],
|
|
629
662
|
purpose: chunk.purpose || '',
|
|
630
663
|
filePath: chunk.filePath,
|
|
631
664
|
files: chunk.filePath ? [chunk.filePath] : [],
|
|
632
665
|
size: chunk.size || 0,
|
|
633
666
|
complexity: chunk.complexity || 0,
|
|
667
|
+
tags: chunk.tags || [],
|
|
634
668
|
startLine: chunk.startLine,
|
|
635
669
|
isExported: chunk.isExported,
|
|
636
670
|
isAsync: chunk.isAsync,
|
|
637
|
-
bundles: chunk.bundles || []
|
|
638
|
-
embedding: chunk.embedding,
|
|
639
|
-
// Also include nested metadata format that VectorVisualization expects
|
|
640
|
-
metadata: {
|
|
641
|
-
content: chunk.code || '',
|
|
642
|
-
semanticType: chunk.subtype || chunk.type || 'unknown',
|
|
643
|
-
businessDomain: chunk.tags || [],
|
|
644
|
-
technicalPatterns: chunk.tags || [],
|
|
645
|
-
purpose: chunk.purpose || '',
|
|
646
|
-
files: chunk.filePath ? [chunk.filePath] : [],
|
|
647
|
-
size: chunk.size || 0,
|
|
648
|
-
complexity: chunk.complexity || 0
|
|
649
|
-
}
|
|
671
|
+
bundles: chunk.bundles || []
|
|
650
672
|
}));
|
|
651
673
|
|
|
652
|
-
// console.log('📊 Transformed chunks sample:', chunks[0] ? {
|
|
653
|
-
// id: chunks[0].id,
|
|
654
|
-
// semanticType: chunks[0].semanticType,
|
|
655
|
-
// hasMetadata: !!chunks[0].metadata
|
|
656
|
-
// } : 'No chunks');
|
|
657
|
-
|
|
658
674
|
this.sendResponse(res, 200, {
|
|
659
675
|
summary: {
|
|
660
676
|
totalFiles: analysis?.summary?.totalFiles || analysis?.fileCount || 0,
|
|
@@ -703,17 +719,78 @@ export default class APIRouter {
|
|
|
703
719
|
}));
|
|
704
720
|
}
|
|
705
721
|
|
|
722
|
+
async handlePostSemanticSearch(req, res) {
|
|
723
|
+
try {
|
|
724
|
+
const body = await this.getRequestBody(req);
|
|
725
|
+
const { query, limit = 20 } = JSON.parse(body);
|
|
726
|
+
|
|
727
|
+
if (!query) {
|
|
728
|
+
return this.sendError(res, 400, 'Search query is required');
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
const results = await this.vectorStore.search(query, { limit });
|
|
732
|
+
this.sendResponse(res, 200, { results });
|
|
733
|
+
} catch (error) {
|
|
734
|
+
this.sendError(res, 500, error.message);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
706
738
|
async handleGetMcpStatus(req, res) {
|
|
739
|
+
const isRunning = this.cntxServer.mcpServerStarted || false;
|
|
707
740
|
const status = {
|
|
708
|
-
enabled:
|
|
741
|
+
enabled: isRunning,
|
|
742
|
+
running: isRunning,
|
|
709
743
|
available: true,
|
|
710
|
-
message: 'MCP server integration available'
|
|
744
|
+
message: isRunning ? 'MCP server is running' : 'MCP server integration available'
|
|
711
745
|
};
|
|
712
746
|
|
|
713
747
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
714
748
|
res.end(JSON.stringify(status));
|
|
715
749
|
}
|
|
716
750
|
|
|
751
|
+
async handleGetVectorDbNetwork(req, res) {
|
|
752
|
+
try {
|
|
753
|
+
// 1. Get all chunks and embeddings
|
|
754
|
+
const chunks = this.configManager.dbManager.db.prepare('SELECT * FROM semantic_chunks').all();
|
|
755
|
+
const embeddings = this.configManager.dbManager.db.prepare('SELECT * FROM vector_embeddings').all();
|
|
756
|
+
|
|
757
|
+
const nodes = chunks.map(c => this.configManager.dbManager.mapChunkRow(c));
|
|
758
|
+
const edges = [];
|
|
759
|
+
const threshold = 0.7; // High threshold for clean network
|
|
760
|
+
|
|
761
|
+
// 2. Perform pairwise similarity on backend (efficient for small/medium repos)
|
|
762
|
+
// Limit to top 100 most complex chunks to keep graph readable
|
|
763
|
+
const topNodes = nodes.sort((a, b) => (b.complexity?.score || 0) - (a.complexity?.score || 0)).slice(0, 100);
|
|
764
|
+
|
|
765
|
+
const embeddingMap = new Map();
|
|
766
|
+
embeddings.forEach(e => {
|
|
767
|
+
embeddingMap.set(e.chunk_id, new Float32Array(e.embedding.buffer, e.embedding.byteOffset, e.embedding.byteLength / 4));
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
for (let i = 0; i < topNodes.length; i++) {
|
|
771
|
+
for (let j = i + 1; j < topNodes.length; j++) {
|
|
772
|
+
const vecA = embeddingMap.get(topNodes[i].id);
|
|
773
|
+
const vecB = embeddingMap.get(topNodes[j].id);
|
|
774
|
+
|
|
775
|
+
if (vecA && vecB) {
|
|
776
|
+
const similarity = this.vectorStore.cosineSimilarity(vecA, vecB);
|
|
777
|
+
if (similarity >= threshold) {
|
|
778
|
+
edges.push({
|
|
779
|
+
source: topNodes[i].id,
|
|
780
|
+
target: topNodes[j].id,
|
|
781
|
+
similarity
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
this.sendResponse(res, 200, { nodes: topNodes, edges });
|
|
789
|
+
} catch (error) {
|
|
790
|
+
this.sendError(res, 500, error.message);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
717
794
|
async handleGetStatus(req, res) {
|
|
718
795
|
const bundles = this.configManager.getBundles();
|
|
719
796
|
const bundleStats = Array.from(bundles.entries()).map(([name, bundle]) => ({
|
|
@@ -742,50 +819,55 @@ export default class APIRouter {
|
|
|
742
819
|
|
|
743
820
|
// === Vector Database Operations ===
|
|
744
821
|
|
|
745
|
-
async
|
|
822
|
+
async handlePostVectorDbSearch(req, res) {
|
|
746
823
|
try {
|
|
824
|
+
const body = await this.getRequestBody(req);
|
|
825
|
+
const { query, limit = 10 } = JSON.parse(body);
|
|
826
|
+
|
|
747
827
|
// Initialize vector store if needed
|
|
748
|
-
if (!this.vectorStore.
|
|
828
|
+
if (!this.vectorStore.initialized) {
|
|
749
829
|
await this.vectorStore.init();
|
|
750
830
|
}
|
|
751
831
|
|
|
752
|
-
const
|
|
753
|
-
this.sendResponse(res, 200,
|
|
832
|
+
const results = await this.vectorStore.search(query, { limit });
|
|
833
|
+
this.sendResponse(res, 200, results);
|
|
754
834
|
} catch (error) {
|
|
755
835
|
this.sendError(res, 500, error.message);
|
|
756
836
|
}
|
|
757
837
|
}
|
|
758
838
|
|
|
759
|
-
async
|
|
839
|
+
async handleGetVectorDbStatus(req, res) {
|
|
760
840
|
try {
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
770
|
-
res.end(JSON.stringify({ success: true, stats }));
|
|
841
|
+
const info = this.configManager.dbManager.getInfo();
|
|
842
|
+
this.sendResponse(res, 200, {
|
|
843
|
+
stats: {
|
|
844
|
+
totalChunks: info.chunkCount,
|
|
845
|
+
embeddingCount: info.embeddingCount,
|
|
846
|
+
modelName: this.vectorStore.modelName
|
|
847
|
+
}
|
|
848
|
+
});
|
|
771
849
|
} catch (error) {
|
|
772
|
-
|
|
773
|
-
res.end(JSON.stringify({ error: error.message }));
|
|
850
|
+
this.sendError(res, 500, error.message);
|
|
774
851
|
}
|
|
775
852
|
}
|
|
776
853
|
|
|
777
|
-
async
|
|
854
|
+
async handlePostVectorDbRebuild(req, res) {
|
|
778
855
|
try {
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
856
|
+
console.log('🔄 Rebuilding vector database...');
|
|
857
|
+
// 1. Get all chunks from SQLite
|
|
858
|
+
const chunks = this.configManager.dbManager.db.prepare('SELECT * FROM semantic_chunks').all()
|
|
859
|
+
.map(row => this.configManager.dbManager.mapChunkRow(row));
|
|
860
|
+
|
|
861
|
+
// 2. Generate/Persist embeddings for every chunk
|
|
862
|
+
for (const chunk of chunks) {
|
|
863
|
+
await this.vectorStore.upsertChunk(chunk);
|
|
785
864
|
}
|
|
786
865
|
|
|
787
|
-
const
|
|
788
|
-
this.sendResponse(res, 200,
|
|
866
|
+
const info = this.configManager.dbManager.getInfo();
|
|
867
|
+
this.sendResponse(res, 200, {
|
|
868
|
+
success: true,
|
|
869
|
+
embeddingCount: info.embeddingCount
|
|
870
|
+
});
|
|
789
871
|
} catch (error) {
|
|
790
872
|
this.sendError(res, 500, error.message);
|
|
791
873
|
}
|
|
@@ -829,13 +911,21 @@ export default class APIRouter {
|
|
|
829
911
|
|
|
830
912
|
async handleGetActivities(req, res) {
|
|
831
913
|
try {
|
|
832
|
-
console.log('API: /api/activities called');
|
|
833
914
|
const activities = await this.activityManager.loadActivities();
|
|
834
|
-
console.log('API: Loaded activities:', activities.length);
|
|
835
915
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
836
916
|
res.end(JSON.stringify(activities));
|
|
837
917
|
} catch (error) {
|
|
838
|
-
|
|
918
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
919
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
async handleGetActivityReasoning(req, res, activityId) {
|
|
924
|
+
try {
|
|
925
|
+
const history = this.configManager.dbManager.getSessionHistory(activityId);
|
|
926
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
927
|
+
res.end(JSON.stringify({ history }));
|
|
928
|
+
} catch (error) {
|
|
839
929
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
840
930
|
res.end(JSON.stringify({ error: error.message }));
|
|
841
931
|
}
|
|
@@ -975,4 +1065,97 @@ export default class APIRouter {
|
|
|
975
1065
|
req.on('error', reject);
|
|
976
1066
|
});
|
|
977
1067
|
}
|
|
1068
|
+
|
|
1069
|
+
async handleGetBundleSyncStatus(req, res) {
|
|
1070
|
+
const bundleStatesPath = path.join(this.configManager.CNTX_DIR, 'bundle-states.json');
|
|
1071
|
+
|
|
1072
|
+
// Since bundles are now only stored in bundle-states.json, we just check if the file exists and is valid
|
|
1073
|
+
let bundleStates = [];
|
|
1074
|
+
let bundleStatesExists = fs.existsSync(bundleStatesPath);
|
|
1075
|
+
|
|
1076
|
+
if (bundleStatesExists) {
|
|
1077
|
+
try {
|
|
1078
|
+
bundleStates = JSON.parse(fs.readFileSync(bundleStatesPath, 'utf8'));
|
|
1079
|
+
} catch (error) {
|
|
1080
|
+
bundleStatesExists = false;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
const details = {
|
|
1085
|
+
inSync: bundleStatesExists && bundleStates.length > 0,
|
|
1086
|
+
bundleCount: bundleStates.length,
|
|
1087
|
+
bundleNames: bundleStates.map(b => b.name),
|
|
1088
|
+
hasValidBundleFile: bundleStatesExists,
|
|
1089
|
+
message: bundleStatesExists
|
|
1090
|
+
? `Found ${bundleStates.length} bundles in bundle-states.json`
|
|
1091
|
+
: 'bundle-states.json file not found or invalid'
|
|
1092
|
+
};
|
|
1093
|
+
|
|
1094
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1095
|
+
res.end(JSON.stringify(details));
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
async handlePostBundleSyncStatus(req, res) {
|
|
1099
|
+
const bundleStatesPath = path.join(this.configManager.CNTX_DIR, 'bundle-states.json');
|
|
1100
|
+
|
|
1101
|
+
if (!fs.existsSync(bundleStatesPath)) {
|
|
1102
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
1103
|
+
res.end(JSON.stringify({ message: 'bundle-states.json not found' }));
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
try {
|
|
1108
|
+
// Validate and reload bundle states to ensure they're in sync with the file
|
|
1109
|
+
const bundleStates = JSON.parse(fs.readFileSync(bundleStatesPath, 'utf8'));
|
|
1110
|
+
|
|
1111
|
+
// Reload bundle states in configuration manager
|
|
1112
|
+
this.configManager.loadBundleStates();
|
|
1113
|
+
|
|
1114
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1115
|
+
res.end(JSON.stringify({
|
|
1116
|
+
message: 'Bundle states reloaded successfully',
|
|
1117
|
+
bundleCount: bundleStates.length,
|
|
1118
|
+
bundles: bundleStates.map(b => b.name)
|
|
1119
|
+
}));
|
|
1120
|
+
} catch (error) {
|
|
1121
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1122
|
+
res.end(JSON.stringify({
|
|
1123
|
+
message: 'Failed to reload bundle states',
|
|
1124
|
+
error: error.message
|
|
1125
|
+
}));
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// === Database API Handlers ===
|
|
1130
|
+
|
|
1131
|
+
async handleGetDatabaseInfo(req, res) {
|
|
1132
|
+
try {
|
|
1133
|
+
const info = this.configManager.dbManager.getInfo();
|
|
1134
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1135
|
+
res.end(JSON.stringify(info));
|
|
1136
|
+
} catch (error) {
|
|
1137
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1138
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
async handlePostDatabaseQuery(req, res) {
|
|
1143
|
+
try {
|
|
1144
|
+
const body = await this.getRequestBody(req);
|
|
1145
|
+
const { query } = JSON.parse(body);
|
|
1146
|
+
|
|
1147
|
+
if (!query || typeof query !== 'string') {
|
|
1148
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1149
|
+
res.end(JSON.stringify({ error: 'Query is required' }));
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
const results = this.configManager.dbManager.query(query);
|
|
1154
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1155
|
+
res.end(JSON.stringify({ results }));
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1158
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
978
1161
|
}
|