plazbot-cli 0.2.3 → 0.2.4

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.
@@ -16,6 +16,7 @@ const templates_1 = require("./templates");
16
16
  const copy_1 = require("./copy");
17
17
  const files_1 = require("./files");
18
18
  const set_1 = require("./set");
19
+ const monitor_1 = require("./monitor");
19
20
  exports.agentCommands = new commander_1.Command('agent')
20
21
  .description('Comandos relacionados con agentes de IA')
21
22
  .addCommand(list_1.listCommand)
@@ -31,4 +32,5 @@ exports.agentCommands = new commander_1.Command('agent')
31
32
  .addCommand(templates_1.templatesCommand)
32
33
  .addCommand(copy_1.copyCommand)
33
34
  .addCommand(files_1.filesCommand)
34
- .addCommand(set_1.setCommand);
35
+ .addCommand(set_1.setCommand)
36
+ .addCommand(monitor_1.monitorCommand);
@@ -0,0 +1,293 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.monitorCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const plazbot_1 = require("plazbot");
9
+ const credentials_1 = require("../../utils/credentials");
10
+ const logger_1 = require("../../utils/logger");
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ const readline_1 = __importDefault(require("readline"));
13
+ const signalr_1 = require("@microsoft/signalr");
14
+ // Colores por tipo de evento (mismos del MonitorTab frontend)
15
+ const EVENT_COLORS = {
16
+ msg_in: { color: '#22d3ee', label: 'msg_in' },
17
+ msg_out: { color: '#4ade80', label: 'msg_out' },
18
+ tool_call: { color: '#c084fc', label: 'tool_call' },
19
+ api_req: { color: '#fbbf24', label: 'api_req' },
20
+ api_res: { color: '#fbbf24', label: 'api_res' },
21
+ action: { color: '#f97316', label: 'action' },
22
+ rag: { color: '#60a5fa', label: 'rag' },
23
+ tokens: { color: '#94a3b8', label: 'tokens' },
24
+ error: { color: '#ef4444', label: 'error' },
25
+ model: { color: '#a78bfa', label: 'model' },
26
+ intent: { color: '#a78bfa', label: 'intent' },
27
+ bot_off: { color: '#f97316', label: 'bot_off' },
28
+ session_field: { color: '#a78bfa', label: 'session_field' },
29
+ };
30
+ const DEFAULT_EVENT_COLOR = { color: '#94a3b8', label: 'unknown' };
31
+ function getBaseUrl(zone, dev) {
32
+ if (dev)
33
+ return 'http://localhost:5090';
34
+ return zone === 'EU' ? 'https://apieu.plazbot.com' : 'https://api.plazbot.com';
35
+ }
36
+ function formatTimestamp(ts) {
37
+ try {
38
+ const d = new Date(ts);
39
+ return d.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
40
+ }
41
+ catch {
42
+ return ts.substring(11, 19);
43
+ }
44
+ }
45
+ function truncate(text, maxLen) {
46
+ if (!text)
47
+ return '';
48
+ return text.length > maxLen ? text.substring(0, maxLen - 3) + '...' : text;
49
+ }
50
+ function shortSession(sessionId) {
51
+ if (!sessionId)
52
+ return '';
53
+ return sessionId.substring(0, 6);
54
+ }
55
+ function renderLogLine(log, opts, lastSessionId) {
56
+ // Modo JSON crudo
57
+ if (opts.jsonMode) {
58
+ return JSON.stringify(log);
59
+ }
60
+ const config = EVENT_COLORS[log.event_type] || DEFAULT_EVENT_COLOR;
61
+ const colorFn = chalk_1.default.hex(config.color);
62
+ const grayFn = chalk_1.default.hex('#71717a');
63
+ const dimFn = chalk_1.default.hex('#52525b');
64
+ let lines = [];
65
+ // Separador de sesion
66
+ if (lastSessionId && log.session_id && log.session_id !== lastSessionId) {
67
+ lines.push(dimFn(' ' + '─'.repeat(16) + ' nueva sesion ' + '─'.repeat(16)));
68
+ }
69
+ // Linea principal
70
+ const timestamp = grayFn(formatTimestamp(log.timestamp));
71
+ const dot = colorFn('●');
72
+ const eventType = colorFn(config.label.padEnd(14));
73
+ const title = chalk_1.default.white(truncate(log.event_title, 80));
74
+ const duration = log.duration_ms > 0 ? dimFn(` ${log.duration_ms}ms`) : '';
75
+ const session = opts.showSession && log.session_id
76
+ ? dimFn(` [${shortSession(log.session_id)}]`)
77
+ : '';
78
+ lines.push(` ${timestamp} ${dot} ${eventType} ${title}${duration}${session}`);
79
+ // Linea de contacto para mensajes
80
+ if (log.contact_name && (log.event_type === 'msg_in' || log.event_type === 'msg_out')) {
81
+ lines.push(dimFn(' | contacto: ') + dimFn(log.contact_name));
82
+ }
83
+ return lines.join('\n');
84
+ }
85
+ function printHeader(agentName) {
86
+ console.log();
87
+ console.log(chalk_1.default.hex('#4CAF50')(' ┌' + '─'.repeat(62) + '┐'));
88
+ console.log(chalk_1.default.hex('#4CAF50')(' │') +
89
+ chalk_1.default.bold(` Monitor`) +
90
+ chalk_1.default.hex('#4ade80')(' ● En vivo') +
91
+ ' '.repeat(38) +
92
+ chalk_1.default.hex('#4CAF50')('│'));
93
+ console.log(chalk_1.default.hex('#4CAF50')(' │') +
94
+ chalk_1.default.gray(` Agente: ${truncate(agentName, 44)}`) +
95
+ ' '.repeat(Math.max(0, 49 - agentName.length)) +
96
+ chalk_1.default.hex('#4CAF50')('│'));
97
+ console.log(chalk_1.default.hex('#4CAF50')(' │') +
98
+ chalk_1.default.gray(' Ctrl+C salir | /filter <tipo> | /clear | /help') +
99
+ ' '.repeat(12) +
100
+ chalk_1.default.hex('#4CAF50')('│'));
101
+ console.log(chalk_1.default.hex('#4CAF50')(' └' + '─'.repeat(62) + '┘'));
102
+ console.log();
103
+ }
104
+ const COMMANDS_HELP = `
105
+ ${chalk_1.default.bold('Comandos disponibles:')}
106
+ ${chalk_1.default.hex('#4CAF50')('/filter <tipo>')} Toggle filtro por tipo (ej: msg_in, error, tool_call)
107
+ ${chalk_1.default.hex('#4CAF50')('/filters')} Mostrar filtros activos
108
+ ${chalk_1.default.hex('#4CAF50')('/clear')} Limpiar pantalla
109
+ ${chalk_1.default.hex('#4CAF50')('/json')} Toggle modo JSON expandido
110
+ ${chalk_1.default.hex('#4CAF50')('/help')} Mostrar estos comandos
111
+
112
+ ${chalk_1.default.bold('Tipos de evento:')}
113
+ ${Object.entries(EVENT_COLORS).map(([key, val]) => ` ${chalk_1.default.hex(val.color)('●')} ${key}`).join('\n')}
114
+ `;
115
+ exports.monitorCommand = new commander_1.Command('monitor')
116
+ .description('Monitor en tiempo real de un agente (logs via SignalR)')
117
+ .requiredOption('-a, --agent-id <id>', 'ID del agente')
118
+ .option('-f, --filter <types>', 'Filtrar por tipos separados por coma (ej: msg_in,msg_out)')
119
+ .option('--no-session', 'Ocultar session ID')
120
+ .option('--json', 'Modo JSON crudo (para piping)', false)
121
+ .option('--dev', 'Usar ambiente de desarrollo', false)
122
+ .action(async (options) => {
123
+ try {
124
+ const credentials = await (0, credentials_1.getStoredCredentials)();
125
+ const baseUrl = getBaseUrl(credentials.zone, options.dev);
126
+ // Filtros iniciales
127
+ const activeFilters = new Set(options.filter ? options.filter.split(',').map(f => f.trim()) : []);
128
+ let jsonMode = options.json;
129
+ let lastSessionId = '';
130
+ // Cargar info del agente
131
+ let agentName = 'Agente';
132
+ try {
133
+ process.stdout.write(chalk_1.default.gray(' Conectando con agente...'));
134
+ const agent = new plazbot_1.Agent({
135
+ workspaceId: credentials.workspace,
136
+ apiKey: credentials.apiKey,
137
+ zone: credentials.zone,
138
+ ...(options.dev && { customUrl: 'http://localhost:5090' }),
139
+ });
140
+ const info = await agent.getAgentById({ id: options.agentId });
141
+ if (info?.name)
142
+ agentName = info.name;
143
+ process.stdout.write('\r' + ' '.repeat(40) + '\r');
144
+ }
145
+ catch {
146
+ process.stdout.write('\r' + ' '.repeat(40) + '\r');
147
+ }
148
+ // Dibujar header
149
+ if (!jsonMode) {
150
+ console.clear();
151
+ printHeader(agentName);
152
+ }
153
+ // Conectar a SignalR
154
+ const hubUrl = `${baseUrl}/agentMonitorHub`;
155
+ const connection = new signalr_1.HubConnectionBuilder()
156
+ .withUrl(hubUrl, {
157
+ skipNegotiation: true,
158
+ transport: signalr_1.HttpTransportType.WebSockets,
159
+ })
160
+ .withAutomaticReconnect([0, 2000, 5000, 10000, 30000])
161
+ .build();
162
+ // Evento de log
163
+ connection.on('agent_log', (log) => {
164
+ // Aplicar filtros
165
+ if (activeFilters.size > 0 && !activeFilters.has(log.event_type))
166
+ return;
167
+ const line = renderLogLine(log, {
168
+ showSession: options.session,
169
+ jsonMode,
170
+ }, lastSessionId);
171
+ console.log(line);
172
+ lastSessionId = log.session_id || lastSessionId;
173
+ });
174
+ // Eventos de conexion
175
+ connection.onreconnecting(() => {
176
+ if (!jsonMode) {
177
+ console.log(chalk_1.default.hex('#FFA726')(' ⟳ Reconectando...'));
178
+ }
179
+ });
180
+ connection.onreconnected(() => {
181
+ if (!jsonMode) {
182
+ console.log(chalk_1.default.hex('#4ade80')(' ● Reconectado'));
183
+ }
184
+ connection.invoke('JoinAgentMonitor', options.agentId).catch(() => { });
185
+ });
186
+ connection.onclose(() => {
187
+ if (!jsonMode) {
188
+ console.log(chalk_1.default.hex('#ef4444')(' ● Desconectado'));
189
+ }
190
+ });
191
+ // Iniciar conexion
192
+ try {
193
+ await connection.start();
194
+ await connection.invoke('JoinAgentMonitor', options.agentId);
195
+ if (!jsonMode) {
196
+ console.log(chalk_1.default.hex('#4ade80')(' ● Conectado al monitor en tiempo real'));
197
+ console.log();
198
+ }
199
+ }
200
+ catch (err) {
201
+ logger_1.logger.error(`No se pudo conectar al monitor: ${err instanceof Error ? err.message : err}`);
202
+ process.exit(1);
203
+ }
204
+ // readline para comandos interactivos
205
+ const rl = readline_1.default.createInterface({
206
+ input: process.stdin,
207
+ output: process.stdout,
208
+ prompt: '',
209
+ });
210
+ // No mostrar prompt para no interferir con el stream
211
+ rl.on('line', (input) => {
212
+ const cmd = input.trim().toLowerCase();
213
+ if (cmd === '/help') {
214
+ console.log(COMMANDS_HELP);
215
+ return;
216
+ }
217
+ if (cmd === '/clear') {
218
+ console.clear();
219
+ printHeader(agentName);
220
+ return;
221
+ }
222
+ if (cmd === '/json') {
223
+ jsonMode = !jsonMode;
224
+ console.log(chalk_1.default.gray(` Modo JSON: ${jsonMode ? 'activado' : 'desactivado'}`));
225
+ return;
226
+ }
227
+ if (cmd === '/filters') {
228
+ if (activeFilters.size === 0) {
229
+ console.log(chalk_1.default.gray(' Sin filtros activos (mostrando todos)'));
230
+ }
231
+ else {
232
+ console.log(chalk_1.default.gray(' Filtros activos: ') + chalk_1.default.white([...activeFilters].join(', ')));
233
+ }
234
+ return;
235
+ }
236
+ if (cmd.startsWith('/filter ')) {
237
+ const filterType = cmd.substring(8).trim();
238
+ if (!filterType) {
239
+ console.log(chalk_1.default.gray(' Uso: /filter <tipo>'));
240
+ return;
241
+ }
242
+ if (activeFilters.has(filterType)) {
243
+ activeFilters.delete(filterType);
244
+ console.log(chalk_1.default.gray(` Filtro removido: ${filterType}`));
245
+ }
246
+ else {
247
+ activeFilters.add(filterType);
248
+ const config = EVENT_COLORS[filterType];
249
+ if (config) {
250
+ console.log(chalk_1.default.hex(config.color)(` ● Filtro agregado: ${filterType}`));
251
+ }
252
+ else {
253
+ console.log(chalk_1.default.gray(` Filtro agregado: ${filterType} (tipo desconocido)`));
254
+ }
255
+ }
256
+ if (activeFilters.size > 0) {
257
+ console.log(chalk_1.default.gray(' Activos: ') + chalk_1.default.white([...activeFilters].join(', ')));
258
+ }
259
+ else {
260
+ console.log(chalk_1.default.gray(' Sin filtros (mostrando todos)'));
261
+ }
262
+ return;
263
+ }
264
+ });
265
+ // Cierre limpio
266
+ const cleanup = async () => {
267
+ if (!jsonMode) {
268
+ console.log(chalk_1.default.gray('\n Cerrando monitor...'));
269
+ }
270
+ try {
271
+ if (connection.state === signalr_1.HubConnectionState.Connected) {
272
+ await connection.invoke('LeaveAgentMonitor', options.agentId);
273
+ }
274
+ await connection.stop();
275
+ }
276
+ catch {
277
+ // Ignorar errores al cerrar
278
+ }
279
+ rl.close();
280
+ if (!jsonMode) {
281
+ console.log(chalk_1.default.gray(' Monitor cerrado.\n'));
282
+ }
283
+ process.exit(0);
284
+ };
285
+ process.on('SIGINT', cleanup);
286
+ process.on('SIGTERM', cleanup);
287
+ }
288
+ catch (error) {
289
+ const message = error instanceof Error ? error.message : 'Error desconocido';
290
+ logger_1.logger.error(message);
291
+ process.exit(1);
292
+ }
293
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plazbot-cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "CLI para Plazbot SDK",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -31,6 +31,7 @@
31
31
  "author": "Kristian Garcia <kristian@plazbot.com>",
32
32
  "license": "MIT",
33
33
  "dependencies": {
34
+ "@microsoft/signalr": "^10.0.0",
34
35
  "@types/inquirer": "^9.0.8",
35
36
  "axios": "^1.6.0",
36
37
  "boxen": "^5.1.2",
@@ -13,6 +13,7 @@ import { templatesCommand } from './templates';
13
13
  import { copyCommand } from './copy';
14
14
  import { filesCommand } from './files';
15
15
  import { setCommand } from './set';
16
+ import { monitorCommand } from './monitor';
16
17
 
17
18
  export const agentCommands = new Command('agent')
18
19
  .description('Comandos relacionados con agentes de IA')
@@ -29,4 +30,5 @@ export const agentCommands = new Command('agent')
29
30
  .addCommand(templatesCommand)
30
31
  .addCommand(copyCommand)
31
32
  .addCommand(filesCommand)
32
- .addCommand(setCommand);
33
+ .addCommand(setCommand)
34
+ .addCommand(monitorCommand);
@@ -0,0 +1,346 @@
1
+ import { Command } from 'commander';
2
+ import { Agent } from 'plazbot';
3
+ import { getStoredCredentials } from '../../utils/credentials';
4
+ import { logger } from '../../utils/logger';
5
+ import { theme } from '../../utils/ui';
6
+ import chalk from 'chalk';
7
+ import readline from 'readline';
8
+ import { HubConnectionBuilder, HttpTransportType, HubConnectionState } from '@microsoft/signalr';
9
+
10
+ // Colores por tipo de evento (mismos del MonitorTab frontend)
11
+ const EVENT_COLORS: Record<string, { color: string; label: string }> = {
12
+ msg_in: { color: '#22d3ee', label: 'msg_in' },
13
+ msg_out: { color: '#4ade80', label: 'msg_out' },
14
+ tool_call: { color: '#c084fc', label: 'tool_call' },
15
+ api_req: { color: '#fbbf24', label: 'api_req' },
16
+ api_res: { color: '#fbbf24', label: 'api_res' },
17
+ action: { color: '#f97316', label: 'action' },
18
+ rag: { color: '#60a5fa', label: 'rag' },
19
+ tokens: { color: '#94a3b8', label: 'tokens' },
20
+ error: { color: '#ef4444', label: 'error' },
21
+ model: { color: '#a78bfa', label: 'model' },
22
+ intent: { color: '#a78bfa', label: 'intent' },
23
+ bot_off: { color: '#f97316', label: 'bot_off' },
24
+ session_field: { color: '#a78bfa', label: 'session_field' },
25
+ };
26
+
27
+ const DEFAULT_EVENT_COLOR = { color: '#94a3b8', label: 'unknown' };
28
+
29
+ interface AgentLogEvent {
30
+ timestamp: string;
31
+ workspace_id: string;
32
+ agent_id: string;
33
+ contact_id: string;
34
+ contact_name: string;
35
+ session_id: string;
36
+ event_type: string;
37
+ event_title: string;
38
+ event_data: string;
39
+ duration_ms: number;
40
+ level: string;
41
+ }
42
+
43
+ function getBaseUrl(zone: string, dev: boolean): string {
44
+ if (dev) return 'http://localhost:5090';
45
+ return zone === 'EU' ? 'https://apieu.plazbot.com' : 'https://api.plazbot.com';
46
+ }
47
+
48
+ function formatTimestamp(ts: string): string {
49
+ try {
50
+ const d = new Date(ts);
51
+ return d.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
52
+ } catch {
53
+ return ts.substring(11, 19);
54
+ }
55
+ }
56
+
57
+ function truncate(text: string, maxLen: number): string {
58
+ if (!text) return '';
59
+ return text.length > maxLen ? text.substring(0, maxLen - 3) + '...' : text;
60
+ }
61
+
62
+ function shortSession(sessionId: string): string {
63
+ if (!sessionId) return '';
64
+ return sessionId.substring(0, 6);
65
+ }
66
+
67
+ function renderLogLine(
68
+ log: AgentLogEvent,
69
+ opts: { showSession: boolean; jsonMode: boolean },
70
+ lastSessionId: string
71
+ ): string {
72
+ // Modo JSON crudo
73
+ if (opts.jsonMode) {
74
+ return JSON.stringify(log);
75
+ }
76
+
77
+ const config = EVENT_COLORS[log.event_type] || DEFAULT_EVENT_COLOR;
78
+ const colorFn = chalk.hex(config.color);
79
+ const grayFn = chalk.hex('#71717a');
80
+ const dimFn = chalk.hex('#52525b');
81
+
82
+ let lines: string[] = [];
83
+
84
+ // Separador de sesion
85
+ if (lastSessionId && log.session_id && log.session_id !== lastSessionId) {
86
+ lines.push(dimFn(' ' + '─'.repeat(16) + ' nueva sesion ' + '─'.repeat(16)));
87
+ }
88
+
89
+ // Linea principal
90
+ const timestamp = grayFn(formatTimestamp(log.timestamp));
91
+ const dot = colorFn('●');
92
+ const eventType = colorFn(config.label.padEnd(14));
93
+ const title = chalk.white(truncate(log.event_title, 80));
94
+ const duration = log.duration_ms > 0 ? dimFn(` ${log.duration_ms}ms`) : '';
95
+ const session = opts.showSession && log.session_id
96
+ ? dimFn(` [${shortSession(log.session_id)}]`)
97
+ : '';
98
+
99
+ lines.push(` ${timestamp} ${dot} ${eventType} ${title}${duration}${session}`);
100
+
101
+ // Linea de contacto para mensajes
102
+ if (log.contact_name && (log.event_type === 'msg_in' || log.event_type === 'msg_out')) {
103
+ lines.push(dimFn(' | contacto: ') + dimFn(log.contact_name));
104
+ }
105
+
106
+ return lines.join('\n');
107
+ }
108
+
109
+ function printHeader(agentName: string) {
110
+ console.log();
111
+ console.log(chalk.hex('#4CAF50')(' ┌' + '─'.repeat(62) + '┐'));
112
+ console.log(
113
+ chalk.hex('#4CAF50')(' │') +
114
+ chalk.bold(` Monitor`) +
115
+ chalk.hex('#4ade80')(' ● En vivo') +
116
+ ' '.repeat(38) +
117
+ chalk.hex('#4CAF50')('│')
118
+ );
119
+ console.log(
120
+ chalk.hex('#4CAF50')(' │') +
121
+ chalk.gray(` Agente: ${truncate(agentName, 44)}`) +
122
+ ' '.repeat(Math.max(0, 49 - agentName.length)) +
123
+ chalk.hex('#4CAF50')('│')
124
+ );
125
+ console.log(
126
+ chalk.hex('#4CAF50')(' │') +
127
+ chalk.gray(' Ctrl+C salir | /filter <tipo> | /clear | /help') +
128
+ ' '.repeat(12) +
129
+ chalk.hex('#4CAF50')('│')
130
+ );
131
+ console.log(chalk.hex('#4CAF50')(' └' + '─'.repeat(62) + '┘'));
132
+ console.log();
133
+ }
134
+
135
+ const COMMANDS_HELP = `
136
+ ${chalk.bold('Comandos disponibles:')}
137
+ ${chalk.hex('#4CAF50')('/filter <tipo>')} Toggle filtro por tipo (ej: msg_in, error, tool_call)
138
+ ${chalk.hex('#4CAF50')('/filters')} Mostrar filtros activos
139
+ ${chalk.hex('#4CAF50')('/clear')} Limpiar pantalla
140
+ ${chalk.hex('#4CAF50')('/json')} Toggle modo JSON expandido
141
+ ${chalk.hex('#4CAF50')('/help')} Mostrar estos comandos
142
+
143
+ ${chalk.bold('Tipos de evento:')}
144
+ ${Object.entries(EVENT_COLORS).map(([key, val]) =>
145
+ ` ${chalk.hex(val.color)('●')} ${key}`
146
+ ).join('\n')}
147
+ `;
148
+
149
+ export const monitorCommand = new Command('monitor')
150
+ .description('Monitor en tiempo real de un agente (logs via SignalR)')
151
+ .requiredOption('-a, --agent-id <id>', 'ID del agente')
152
+ .option('-f, --filter <types>', 'Filtrar por tipos separados por coma (ej: msg_in,msg_out)')
153
+ .option('--no-session', 'Ocultar session ID')
154
+ .option('--json', 'Modo JSON crudo (para piping)', false)
155
+ .option('--dev', 'Usar ambiente de desarrollo', false)
156
+ .action(async (options: {
157
+ agentId: string;
158
+ filter?: string;
159
+ session: boolean;
160
+ json: boolean;
161
+ dev: boolean;
162
+ }) => {
163
+ try {
164
+ const credentials = await getStoredCredentials();
165
+ const baseUrl = getBaseUrl(credentials.zone, options.dev);
166
+
167
+ // Filtros iniciales
168
+ const activeFilters: Set<string> = new Set(
169
+ options.filter ? options.filter.split(',').map(f => f.trim()) : []
170
+ );
171
+ let jsonMode = options.json;
172
+ let lastSessionId = '';
173
+
174
+ // Cargar info del agente
175
+ let agentName = 'Agente';
176
+ try {
177
+ process.stdout.write(chalk.gray(' Conectando con agente...'));
178
+ const agent = new Agent({
179
+ workspaceId: credentials.workspace,
180
+ apiKey: credentials.apiKey,
181
+ zone: credentials.zone,
182
+ ...(options.dev && { customUrl: 'http://localhost:5090' }),
183
+ });
184
+ const info: any = await agent.getAgentById({ id: options.agentId });
185
+ if (info?.name) agentName = info.name;
186
+ process.stdout.write('\r' + ' '.repeat(40) + '\r');
187
+ } catch {
188
+ process.stdout.write('\r' + ' '.repeat(40) + '\r');
189
+ }
190
+
191
+ // Dibujar header
192
+ if (!jsonMode) {
193
+ console.clear();
194
+ printHeader(agentName);
195
+ }
196
+
197
+ // Conectar a SignalR
198
+ const hubUrl = `${baseUrl}/agentMonitorHub`;
199
+ const connection = new HubConnectionBuilder()
200
+ .withUrl(hubUrl, {
201
+ skipNegotiation: true,
202
+ transport: HttpTransportType.WebSockets,
203
+ })
204
+ .withAutomaticReconnect([0, 2000, 5000, 10000, 30000])
205
+ .build();
206
+
207
+ // Evento de log
208
+ connection.on('agent_log', (log: AgentLogEvent) => {
209
+ // Aplicar filtros
210
+ if (activeFilters.size > 0 && !activeFilters.has(log.event_type)) return;
211
+
212
+ const line = renderLogLine(log, {
213
+ showSession: options.session,
214
+ jsonMode,
215
+ }, lastSessionId);
216
+
217
+ console.log(line);
218
+ lastSessionId = log.session_id || lastSessionId;
219
+ });
220
+
221
+ // Eventos de conexion
222
+ connection.onreconnecting(() => {
223
+ if (!jsonMode) {
224
+ console.log(chalk.hex('#FFA726')(' ⟳ Reconectando...'));
225
+ }
226
+ });
227
+
228
+ connection.onreconnected(() => {
229
+ if (!jsonMode) {
230
+ console.log(chalk.hex('#4ade80')(' ● Reconectado'));
231
+ }
232
+ connection.invoke('JoinAgentMonitor', options.agentId).catch(() => {});
233
+ });
234
+
235
+ connection.onclose(() => {
236
+ if (!jsonMode) {
237
+ console.log(chalk.hex('#ef4444')(' ● Desconectado'));
238
+ }
239
+ });
240
+
241
+ // Iniciar conexion
242
+ try {
243
+ await connection.start();
244
+ await connection.invoke('JoinAgentMonitor', options.agentId);
245
+ if (!jsonMode) {
246
+ console.log(chalk.hex('#4ade80')(' ● Conectado al monitor en tiempo real'));
247
+ console.log();
248
+ }
249
+ } catch (err) {
250
+ logger.error(`No se pudo conectar al monitor: ${err instanceof Error ? err.message : err}`);
251
+ process.exit(1);
252
+ }
253
+
254
+ // readline para comandos interactivos
255
+ const rl = readline.createInterface({
256
+ input: process.stdin,
257
+ output: process.stdout,
258
+ prompt: '',
259
+ });
260
+
261
+ // No mostrar prompt para no interferir con el stream
262
+ rl.on('line', (input: string) => {
263
+ const cmd = input.trim().toLowerCase();
264
+
265
+ if (cmd === '/help') {
266
+ console.log(COMMANDS_HELP);
267
+ return;
268
+ }
269
+
270
+ if (cmd === '/clear') {
271
+ console.clear();
272
+ printHeader(agentName);
273
+ return;
274
+ }
275
+
276
+ if (cmd === '/json') {
277
+ jsonMode = !jsonMode;
278
+ console.log(chalk.gray(` Modo JSON: ${jsonMode ? 'activado' : 'desactivado'}`));
279
+ return;
280
+ }
281
+
282
+ if (cmd === '/filters') {
283
+ if (activeFilters.size === 0) {
284
+ console.log(chalk.gray(' Sin filtros activos (mostrando todos)'));
285
+ } else {
286
+ console.log(chalk.gray(' Filtros activos: ') + chalk.white([...activeFilters].join(', ')));
287
+ }
288
+ return;
289
+ }
290
+
291
+ if (cmd.startsWith('/filter ')) {
292
+ const filterType = cmd.substring(8).trim();
293
+ if (!filterType) {
294
+ console.log(chalk.gray(' Uso: /filter <tipo>'));
295
+ return;
296
+ }
297
+ if (activeFilters.has(filterType)) {
298
+ activeFilters.delete(filterType);
299
+ console.log(chalk.gray(` Filtro removido: ${filterType}`));
300
+ } else {
301
+ activeFilters.add(filterType);
302
+ const config = EVENT_COLORS[filterType];
303
+ if (config) {
304
+ console.log(chalk.hex(config.color)(` ● Filtro agregado: ${filterType}`));
305
+ } else {
306
+ console.log(chalk.gray(` Filtro agregado: ${filterType} (tipo desconocido)`));
307
+ }
308
+ }
309
+ if (activeFilters.size > 0) {
310
+ console.log(chalk.gray(' Activos: ') + chalk.white([...activeFilters].join(', ')));
311
+ } else {
312
+ console.log(chalk.gray(' Sin filtros (mostrando todos)'));
313
+ }
314
+ return;
315
+ }
316
+ });
317
+
318
+ // Cierre limpio
319
+ const cleanup = async () => {
320
+ if (!jsonMode) {
321
+ console.log(chalk.gray('\n Cerrando monitor...'));
322
+ }
323
+ try {
324
+ if (connection.state === HubConnectionState.Connected) {
325
+ await connection.invoke('LeaveAgentMonitor', options.agentId);
326
+ }
327
+ await connection.stop();
328
+ } catch {
329
+ // Ignorar errores al cerrar
330
+ }
331
+ rl.close();
332
+ if (!jsonMode) {
333
+ console.log(chalk.gray(' Monitor cerrado.\n'));
334
+ }
335
+ process.exit(0);
336
+ };
337
+
338
+ process.on('SIGINT', cleanup);
339
+ process.on('SIGTERM', cleanup);
340
+
341
+ } catch (error) {
342
+ const message = error instanceof Error ? error.message : 'Error desconocido';
343
+ logger.error(message);
344
+ process.exit(1);
345
+ }
346
+ });