plazbot-cli
Version:
CLI para Plazbot SDK
213 lines (179 loc) • 7.96 kB
text/typescript
import { Command } from 'commander';
import { Agent } from 'plazbot';
import { getStoredCredentials } from '../../utils/credentials';
import { logger } from '../../utils/logger';
import { AgentCommandOptions, AgentResponse, AgentSource } from '../../types/agent';
import { theme, section } from '../../utils/ui';
import crypto from 'crypto';
import readline from 'readline';
import chalk from 'chalk';
const COMMANDS_HELP = `
${chalk.bold('Comandos disponibles:')}
${chalk.hex('#4CAF50')('/exit')} Terminar conversacion
${chalk.hex('#4CAF50')('/clear')} Limpiar pantalla
${chalk.hex('#4CAF50')('/info')} Informacion de la sesion
${chalk.hex('#4CAF50')('/sources')} Mostrar/ocultar fuentes
${chalk.hex('#4CAF50')('/help')} Mostrar estos comandos
`;
export const chatCommand = new Command('chat')
.description('Inicia una sesion de chat interactiva con un agente')
.requiredOption('-a, --agent-id <id>', 'ID del agente')
.option('-s, --session-id <id>', 'ID de sesion (opcional)')
.option('-m, --multiple-answers', 'Permitir multiples respuestas', false)
.option('--dev', 'Usar ambiente de desarrollo', false)
.action(async (options: AgentCommandOptions & {
agentId: string;
sessionId?: string;
multipleAnswers?: boolean;
}) => {
try {
const credentials = await getStoredCredentials();
const agent = new Agent({
workspaceId: credentials.workspace,
apiKey: credentials.apiKey,
zone: credentials.zone,
...(options.dev && { customUrl: "http://localhost:5090" })
});
const sessionId = options.sessionId || crypto.randomUUID();
let showSources = true;
// Cargar info del agente
let agentInfo: any = null;
try {
process.stdout.write(chalk.gray(' Conectando con agente...'));
agentInfo = await agent.getAgentById({ id: options.agentId });
process.stdout.write('\r' + ' '.repeat(40) + '\r');
} catch {
process.stdout.write('\r' + ' '.repeat(40) + '\r');
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// Pantalla de chat
console.clear();
const agentName = agentInfo?.name || 'Agente';
const toolCalling = agentInfo?.useToolCalling ? chalk.hex('#4CAF50')(' [Tool Calling]') : '';
console.log();
console.log(chalk.hex('#4CAF50')(' ┌' + '─'.repeat(58) + '┐'));
console.log(chalk.hex('#4CAF50')(' │') + chalk.bold(` ${agentName}${toolCalling}`).padEnd(68) + chalk.hex('#4CAF50')('│'));
console.log(chalk.hex('#4CAF50')(' │') + chalk.gray(` Session: ${sessionId.substring(0, 8)}...`).padEnd(68) + chalk.hex('#4CAF50')('│'));
console.log(chalk.hex('#4CAF50')(' │') + chalk.gray(' /help para ver comandos').padEnd(68) + chalk.hex('#4CAF50')('│'));
console.log(chalk.hex('#4CAF50')(' └' + '─'.repeat(58) + '┘'));
console.log();
// Saludo del agente
if (agentInfo?.instructions?.greeting) {
const timestamp = new Date().toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' });
console.log(chalk.hex('#4CAF50')(` ${agentName}`) + chalk.gray(` ${timestamp}`));
console.log(chalk.white(` ${agentInfo.instructions.greeting}`));
console.log();
}
const askQuestion = () => {
rl.question(chalk.hex('#2196F3')(' Tu > '), async (question) => {
if (!question.trim()) {
askQuestion();
return;
}
// Comandos especiales
if (question.toLowerCase() === '/exit') {
console.log(chalk.gray('\n Sesion terminada.\n'));
rl.close();
return;
}
if (question.toLowerCase() === '/clear') {
console.clear();
askQuestion();
return;
}
if (question.toLowerCase() === '/help') {
console.log(COMMANDS_HELP);
askQuestion();
return;
}
if (question.toLowerCase() === '/info') {
console.log(section('Informacion de sesion'));
logger.label('Agent ID', options.agentId);
logger.label('Session ID', sessionId);
logger.label('Agente', agentName);
logger.label('Tool Calling', agentInfo?.useToolCalling ? 'Activado' : 'Desactivado');
logger.label('AI Provider', agentInfo?.customAIConfig ? (agentInfo.aiProviders?.[0]?.provider || 'Custom') : 'Default');
console.log();
askQuestion();
return;
}
if (question.toLowerCase() === '/sources') {
showSources = !showSources;
console.log(chalk.gray(` Fuentes: ${showSources ? 'activadas' : 'desactivadas'}`));
console.log();
askQuestion();
return;
}
try {
process.stdout.write(chalk.gray(' ...pensando\n'));
const response = await agent.onMessage({
agentId: options.agentId,
question,
sessionId,
multipleAnswers: options.multipleAnswers
}) as AgentResponse & { actionsExecuted?: any[] };
const timestamp = new Date().toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' });
// Mostrar tool calls si hay
if (response.actionsExecuted && response.actionsExecuted.length > 0) {
response.actionsExecuted.forEach((action: any) => {
console.log(chalk.hex('#FFA726')(` ⚡ Tool: ${action.name || action.intent || 'action'}`));
});
}
// Respuesta del agente
console.log();
console.log(chalk.hex('#4CAF50')(` ${agentName}`) + chalk.gray(` ${timestamp}`));
// Formatear respuesta (soporte basico de markdown)
const formattedAnswer = formatResponse(response.answer);
console.log(formattedAnswer);
console.log();
// Fuentes
if (showSources && response.sources && response.sources.length > 0) {
console.log(chalk.gray(' Fuentes:'));
response.sources.forEach((source: AgentSource) => {
console.log(chalk.gray(` - ${source.title || 'Sin titulo'}`));
if (source.url) console.log(chalk.gray(` ${source.url}`));
});
console.log();
}
askQuestion();
} catch (error) {
const message = error instanceof Error ? error.message : 'Error desconocido';
console.log(chalk.hex('#EF5350')(`\n ✖ Error: ${message}\n`));
askQuestion();
}
});
};
askQuestion();
} catch (error) {
const message = error instanceof Error ? error.message : 'Error desconocido';
logger.error(message);
process.exit(1);
}
});
function formatResponse(text: string): string {
if (!text) return '';
return text.split('\n').map(line => {
// Headers
if (line.startsWith('### ')) return chalk.bold.hex('#4CAF50')(' ' + line.substring(4));
if (line.startsWith('## ')) return chalk.bold.hex('#4CAF50')(' ' + line.substring(3));
if (line.startsWith('# ')) return chalk.bold.hex('#4CAF50')(' ' + line.substring(2));
// Bold
line = line.replace(/\*\*(.*?)\*\*/g, (_, text) => chalk.bold(text));
// Italic
line = line.replace(/\*(.*?)\*/g, (_, text) => chalk.italic(text));
// Code inline
line = line.replace(/`(.*?)`/g, (_, text) => chalk.hex('#FFA726')(text));
// List items
if (line.match(/^\s*[-*]\s/)) {
return chalk.white(' ' + line.replace(/^\s*[-*]\s/, ' • '));
}
// Numbered lists
if (line.match(/^\s*\d+\.\s/)) {
return chalk.white(' ' + line);
}
return chalk.white(' ' + line);
}).join('\n');
}