UNPKG

trace.ai-cli

Version:

A powerful AI-powered CLI tool

157 lines (134 loc) 5.47 kB
const WebSocket = require('ws'); const chalk = require('chalk'); const { processWithAI } = require('./aiService'); const { markdownToAnsi } = require('../utils/markdown'); class AgentHandler { constructor(cli) { this.cli = cli; this.ws = null; this._manualDisconnect = false; } get connected() { return this.ws && this.ws.readyState === WebSocket.OPEN; } async connect(url) { if (this.ws) { console.log(chalk.yellow('Already connected! Disconnect first using /exit.')); return; } try { console.log(chalk.blueBright(`Connecting to ${url}...`)); this.ws = new WebSocket(url); this.ws.on('open', () => { this.cli.safeLog( chalk.green(' Connected to Keylink agent successfully!') + '\n' + chalk.gray('Type your task or @trained agent. Type /exit to disconnect.') ); }); this.ws.on('message', (data) => this._handleMessage(data)); this.ws.on('close', () => { if (!this._manualDisconnect) { this.cli.safeLog(chalk.yellow('\nAgent connection closed.')); } this.ws = null; this._manualDisconnect = false; }); this.ws.on('error', (err) => { this.cli.safeLog(chalk.red(`\nKeylink Connection Error: ${err.message}`)); this.ws = null; }); } catch (error) { this.cli.displayError(`Failed to connect: ${error.message}`); this.ws = null; } } send(query) { if (!this.connected) return; this.ws.send(JSON.stringify({ type: 'agent', query })); } disconnect() { if (this.ws) { this._manualDisconnect = true; this.ws.close(); this.ws = null; this.cli._agentCache = []; this.cli.safeLog(chalk.green(' Disconnected from agent.')); } } _handleMessage(data) { try { const parsed = JSON.parse(data.toString()); if (parsed.type === 'keepalive') return; if (parsed.type === 'agent_response') { this._handleAgentResponse(parsed); } else if (parsed.type === 'AI_AGENT_DONE' && parsed.summary) { const formatted = markdownToAnsi(parsed.summary); this.cli.displayResult('Agent Complete', formatted, null); } else if (parsed.type === 'AI_AGENT_STEP' && parsed.action) { const stepInfo = chalk.cyan(`[Step ${parsed.step}/${parsed.maxSteps}]`); const action = chalk.yellow(parsed.action.action); this.cli.safeLog(`\n${stepInfo} ${action}: ${chalk.gray(JSON.stringify(parsed.action, null, 2))}`); } else if (parsed.type === 'AI_AGENT_STATUS') { const status = parsed.status === 'started' ? chalk.green(' Started') : chalk.yellow(parsed.status); this.cli.safeLog(`\n${chalk.cyan('[Status]')} ${status}${parsed.query ? ': ' + chalk.white(parsed.query) : ''}`); } else if (parsed.type === 'AI_AGENT_VALIDATION') { const success = parsed.validation?.success ? chalk.green('') : chalk.red(''); this.cli.safeLog(`\n${chalk.cyan('[Validation]')} ${success} ${chalk.gray(parsed.validation?.reason || '')}`); } else { const marker = chalk.yellow('[Agent Response]:'); this.cli.safeLog(`\n${marker} ${chalk.white(data.toString())}`); } } catch (e) { const marker = chalk.yellow('[Agent Response]:'); this.cli.safeLog(`\n${marker} ${chalk.white(data.toString())}`); } } _handleAgentResponse(parsed) { if (parsed.answer) { const formatted = markdownToAnsi(parsed.answer); this.cli.displayResult('Trace.Ai Response', formatted, null); return; } if (parsed.context || parsed.results) { const frames = ['', '', '', '', '', '', '', '', '', '']; let frameIndex = 0; const spinner = setInterval(() => { process.stdout.write(`\r${chalk.blueBright(frames[frameIndex])} ${chalk.white('Processing your query')}`); frameIndex = (frameIndex + 1) % frames.length; }, 100); let contextText = ''; if (parsed.context) { contextText += parsed.context + '\n\n'; } if (parsed.results && Array.isArray(parsed.results)) { contextText += 'Search Results:\n'; parsed.results.forEach((result, idx) => { contextText += `\n[${idx + 1}] ${result.metadata?.tabTitle || 'Result'}\n`; if (result.metadata?.url) contextText += `URL: ${result.metadata.url}\n`; if (result.text) contextText += `${result.text}\n`; }); } (async () => { try { const query = parsed.query || 'Analyze this information'; const aiResponse = await processWithAI(query, contextText, this.cli.aiMode); clearInterval(spinner); process.stdout.write(`\r${chalk.green('')} ${chalk.white('Processing your query')} ${chalk.gray('- Complete!')}\n`); const formatted = markdownToAnsi(aiResponse.text || aiResponse); this.cli.displayResult('Trace.Ai Response', formatted, null); } catch (error) { clearInterval(spinner); process.stdout.write(`\r${chalk.red('')} ${chalk.white('Processing your query')} ${chalk.gray('- Failed!')}\n`); this.cli.displayError(error.message); } })(); return; } if (parsed.message) { if (!parsed.message.includes('Found') && !parsed.message.includes('context')) { this.cli.safeLog('\n' + chalk.gray(parsed.message)); } } } } module.exports = AgentHandler;