File size: 6,303 Bytes
529090e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import { Request, Response, Router } from 'express';
import { OmniHarvester } from './OmniHarvester.js';
import { knowledgeAcquisition } from '../KnowledgeAcquisitionService.js';
import path from 'path';

const router = Router();
const harvester = new OmniHarvester(path.resolve(process.cwd(), '../../')); // Scan project root

// --- HARVEST CONTROL STATE ---
let isHarvesting = false;
let currentHarvestId: string | null = null;
let harvestStats = {
    projectFiles: 0,
    intelFiles: 0,
    startTime: 0
};

// 1. Harvest Status
router.get('/harvest/status', (req, res) => {
    res.json({
        isRunning: isHarvesting,
        harvestId: currentHarvestId,
        canAbort: isHarvesting,
        duration: isHarvesting ? Date.now() - harvestStats.startTime : 0
    });
});

// 2. Harvest Summary
router.get('/harvest/summary', async (req, res) => {
    // In a real scenario, query Neo4j for accurate counts.
    // For now, return tracking stats.
    try {
        const vectorStats = await knowledgeAcquisition.getVectorStats();
        
        res.json({
            project: { 
                totalFiles: harvestStats.projectFiles, 
                byStrategy: { 'code': harvestStats.projectFiles } 
            },
            intel: { 
                totalFiles: vectorStats.totalRecords || harvestStats.intelFiles, 
                byStrategy: { 'web': vectorStats.totalRecords || 0 } 
            },
            totalFiles: harvestStats.projectFiles + (vectorStats.totalRecords || 0)
        });
    } catch (err) {
        res.json({
            project: { totalFiles: 0, byStrategy: {} },
            intel: { totalFiles: 0, byStrategy: {} },
            totalFiles: 0
        });
    }
});

// 3. Start Harvest (Project)
router.post('/harvest', async (req, res) => {
    if (isHarvesting) return res.status(409).json({ error: 'Harvest already in progress' });
    
    isHarvesting = true;
    currentHarvestId = `h-${Date.now()}`;
    harvestStats.startTime = Date.now();

    // Start async process
    (async () => {
        try {
            // Use OmniHarvester to scan file system
            const nodes = await harvester.scan(); 
            // TODO: Actually ingest these nodes into KnowledgeAcquisition
            harvestStats.projectFiles = nodes.length; // Simplified count
        } catch (e) {
            console.error("Harvest failed:", e);
        } finally {
            isHarvesting = false;
            currentHarvestId = null;
        }
    })();

    res.json({ message: 'Project harvest started', harvestId: currentHarvestId });
});

// 4. Start Harvest (Intel / Targets)
router.post('/harvest/intel', async (req, res) => {
    if (isHarvesting) return res.status(409).json({ error: 'Harvest already in progress' });

    isHarvesting = true;
    currentHarvestId = `i-${Date.now()}`;
    harvestStats.startTime = Date.now();

    (async () => {
        try {
            const results = await knowledgeAcquisition.acquireFromTargets();
            harvestStats.intelFiles += results.filter(r => r.success).length;
        } catch (e) {
            console.error("Intel harvest failed:", e);
        } finally {
            isHarvesting = false;
            currentHarvestId = null;
        }
    })();

    res.json({ message: 'Intel harvest started', harvestId: currentHarvestId });
});

// 5. Full Sweep
router.post('/harvest/all', async (req, res) => {
    if (isHarvesting) return res.status(409).json({ error: 'Harvest already in progress' });

    isHarvesting = true;
    currentHarvestId = `f-${Date.now()}`;
    
    (async () => {
        try {
            // 1. Project
            const nodes = await harvester.scan();
            harvestStats.projectFiles = nodes.length;
            
            // 2. Intel
            const results = await knowledgeAcquisition.acquireFromTargets();
            harvestStats.intelFiles += results.filter(r => r.success).length;
        } finally {
            isHarvesting = false;
            currentHarvestId = null;
        }
    })();

    res.json({ message: 'Full sweep started', harvestId: currentHarvestId });
});

// 6. Abort / Nødstop
router.post('/harvest/abort', (req, res) => {
    if (!isHarvesting) return res.status(400).json({ message: 'No active harvest to abort' });
    
    // Logic to kill the async process would go here (requires AbortController implementation in services)
    isHarvesting = false;
    currentHarvestId = null;
    
    res.json({ success: true, message: 'Harvest aborted immediately' });
});


router.get('/graph', async (req: Request, res: Response) => {
  try {
    // In a real implementation, this would query Neo4j.
    // For the prototype, we scan the file system live and return a tree structure
    // which the frontend can visualize as a graph.
    const nodes = await harvester.scan();
    
    // Flatten the tree for the 3D graph (simplified)
    // This maps the recursive structure to a flat node/link list
    const graphData = {
        nodes: [] as any[],
        links: [] as any[]
    };

    let idCounter = 0;
    function processNode(node: any, parentId: number | null = null) {
        const currentId = idCounter++;
        const isDir = node.type === 'directory';
        
        graphData.nodes.push({
            id: currentId,
            name: node.name,
            type: node.type,
            val: isDir ? 5 : 1, // Size for visualizer
            color: isDir ? '#ff00ff' : '#00B5CB'
        });

        if (parentId !== null) {
            graphData.links.push({
                source: parentId,
                target: currentId
            });
        }

        if (node.children) {
            node.children.forEach((child: any) => processNode(child, currentId));
        }
    }

    // Only process first level depth for performance in this mocked version if it's huge
    nodes.forEach(node => processNode(node, null));

    res.json(graphData);
  } catch (error: any) {
    console.error("Evolution Graph Error:", error);
    res.status(500).json({ error: error.message });
  }
});

router.post('/evolve', async (req: Request, res: Response) => {
    // Placeholder for self-modification endpoint
    res.json({ message: "Evolution request received. Analysis started." });
});

export const evolutionRouter = router;