import * as http from 'http'; import * as fs from 'fs'; import * as path from 'path'; import axios from 'axios'; import { getConfig, _getBrainDir, _isBrainDirExplicitlySet, findBrainFiles } from './utils'; export interface BridgeInterface { injectSystemMessage(msg: string): void; getHistoryText(): string; sendPromptFromExtension(prompt: string): void; brainEnabled: boolean; findBrainFiles(dir: string): string[]; } export class BridgeServer { private server: http.Server | null = null; constructor(private provider: BridgeInterface) {} public start(port: number = 4825) { this.server = http.createServer((req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } const url = req.url || ''; if (req.method === 'GET' && url === '/ping') { this.handlePing(res); } else if (req.method === 'POST' && url === '/api/exam') { this.handlePost(req, res, this.processExam.bind(this)); } else if (req.method === 'POST' && url === '/api/evaluate') { this.handlePost(req, res, this.processEvaluate.bind(this)); } else if (req.method === 'GET' && url === '/api/evaluate-history') { this.processEvaluateHistory(res); } else if (req.method === 'POST' && url === '/api/brain-inject') { this.handlePost(req, res, this.processBrainInject.bind(this)); } else { res.writeHead(404); res.end(); } }); this.server.listen(port, '127.0.0.1', () => { console.log(`[G1nation] Bridge Server active on port ${port}`); }); } private handlePing(res: http.ServerResponse) { const brainDir = _getBrainDir(); const brainCount = fs.existsSync(brainDir) ? findBrainFiles(brainDir).length : 0; res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ok', msg: 'G1nation Bridge Ready', config: getConfig(), brain: { fileCount: brainCount, enabled: this.provider.brainEnabled } })); } private handlePost(req: http.IncomingMessage, res: http.ServerResponse, processor: (data: any, res: http.ServerResponse) => Promise) { let body = ''; req.on('data', chunk => body += chunk.toString()); req.on('end', async () => { try { const parsed = JSON.parse(body); await processor(parsed, res); } catch (e: any) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: e.message })); } }); } private async processExam(data: any, res: http.ServerResponse) { const prompt = data.prompt || 'Automatic Prompt Received'; this.provider.sendPromptFromExtension(`[Bridge Input] ${prompt}`); const result = await this.callAI(prompt); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true, rawOutput: result })); } private async processEvaluate(data: any, res: http.ServerResponse) { const prompt = data.prompt || ''; this.provider.injectSystemMessage(`**[A.U Evaluation Started]**\nAnalyzing input: _"${prompt.substring(0, 60)}..."_`); const evaluationPrompt = `[EVALUATION REQUEST]\nPlease evaluate the following input and provide a score/reasoning:\n\n${prompt}`; const result = await this.callAI(evaluationPrompt); this.provider.injectSystemMessage(`**[Evaluation Complete]**\n${result.substring(0, 300)}...`); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ rawOutput: result })); } private async processEvaluateHistory(res: http.ServerResponse) { const historyText = this.provider.getHistoryText(); if (!historyText || historyText.length < 50) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: "Insufficient chat history for evaluation." })); return; } this.provider.injectSystemMessage(`**[History Evaluation]** Analyzing conversation flow...`); const historyPrompt = `Analyze this conversation history and return a JSON score for Math, Logic, Creative, and Code (0-100):\n\n${historyText.slice(-6000)}`; const result = await this.callAI(historyPrompt); const jsonMatch = result.match(/\{[\s\S]*?\}/); if (jsonMatch) { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(jsonMatch[0]); } else { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: "Failed to parse evaluation JSON", raw: result })); } } private async processBrainInject(data: any, res: http.ServerResponse) { const { title, markdown, prompt } = data; let brainDir = _getBrainDir(); if (!fs.existsSync(brainDir)) { fs.mkdirSync(brainDir, { recursive: true }); } const today = new Date().toISOString().split('T')[0]; const datePath = path.join(brainDir, '00_Raw', today); fs.mkdirSync(datePath, { recursive: true }); const safeTitle = title.replace(/[^a-zA-Z0-9가-힣]/gi, '_'); const filePath = path.join(datePath, `${safeTitle}.md`); fs.writeFileSync(filePath, markdown, 'utf-8'); this.provider.injectSystemMessage(`**[Brain Inject]** Knowledge captured: ${title}`); const result = await this.callAI(prompt || `Analyze this new knowledge: ${title}`); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true, rawOutput: result })); } private async callAI(prompt: string): Promise { const config = getConfig(); const isLMStudio = config.ollamaUrl.includes('1234') || config.ollamaUrl.includes('v1'); const apiUrl = isLMStudio ? `${config.ollamaUrl}/v1/chat/completions` : `${config.ollamaUrl}/api/chat`; const payload = { model: config.defaultModel, messages: [{ role: 'user', content: prompt }], stream: false }; const res = await axios.post(apiUrl, payload, { timeout: config.timeout }); return isLMStudio ? (res.data.choices?.[0]?.message?.content || '') : (res.data.message?.content || ''); } }