plazbot-cli 0.2.4 → 0.2.6

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.
@@ -73,7 +73,7 @@ exports.createCommand = new commander_1.Command('create')
73
73
  }
74
74
  else {
75
75
  // Modo wizard interactivo
76
- agentConfig = await (0, wizard_1.runAgentWizard)(credentials.zone);
76
+ agentConfig = await (0, wizard_1.runAgentWizard)(credentials.zone, credentials.workspace, credentials.apiKey, options.dev);
77
77
  const inquirer = await Promise.resolve().then(() => __importStar(require('inquirer')));
78
78
  const { confirm } = await inquirer.default.prompt([{
79
79
  type: 'confirm',
@@ -99,7 +99,9 @@ exports.createCommand = new commander_1.Command('create')
99
99
  }
100
100
  logger_1.logger.label('Tool Calling', agentConfig.useToolCalling ? 'Activado' : 'Desactivado');
101
101
  if (agentConfig.channels && agentConfig.channels.length > 0) {
102
- logger_1.logger.label('WhatsApp', agentConfig.channels[0].key);
102
+ agentConfig.channels.forEach((ch) => {
103
+ logger_1.logger.label(`Canal (${ch.channel})`, ch.key);
104
+ });
103
105
  }
104
106
  console.log();
105
107
  logger_1.logger.dim('Siguiente paso: plazbot agent chat -a ' + (result.agentId || '<agentId>'));
@@ -1,153 +1,188 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.getCommand = void 0;
4
7
  const commander_1 = require("commander");
5
8
  const plazbot_1 = require("plazbot");
6
9
  const credentials_1 = require("../../utils/credentials");
7
10
  const logger_1 = require("../../utils/logger");
11
+ const chalk_1 = __importDefault(require("chalk"));
8
12
  exports.getCommand = new commander_1.Command('get')
9
13
  .description('Obtiene información detallada de un agente específico')
10
14
  .argument('<agentId>', 'ID del agente a consultar')
15
+ .option('-w, --workspace <id>', 'Workspace ID (sobreescribe config local)')
16
+ .option('-z, --zone <zone>', 'Zona LA o EU (sobreescribe config local)')
17
+ .option('--raw', 'Mostrar el JSON completo sin formatear', false)
11
18
  .option('--dev', 'Usar ambiente de desarrollo', false)
12
19
  .action(async (agentId, options) => {
13
20
  try {
14
- // Obtener credenciales guardadas
15
21
  const credentials = await (0, credentials_1.getStoredCredentials)();
16
- // Crear instancia del agente con las credenciales guardadas
22
+ const effectiveWorkspace = options.workspace || credentials.workspace;
23
+ const effectiveZone = (options.zone?.toUpperCase() === 'EU' ? 'EU' : options.zone?.toUpperCase() === 'LA' ? 'LA' : credentials.zone);
24
+ if (options.workspace || options.zone) {
25
+ console.log(chalk_1.default.hex('#FFA726')(`\n Modo soporte: workspace=${effectiveWorkspace} zona=${effectiveZone}`));
26
+ }
17
27
  const agent = new plazbot_1.Agent({
18
- workspaceId: credentials.workspace,
28
+ workspaceId: effectiveWorkspace,
19
29
  apiKey: credentials.apiKey,
20
- zone: credentials.zone,
30
+ zone: effectiveZone,
21
31
  ...(options.dev && { customUrl: "http://localhost:5090" })
22
32
  });
23
- // Obtener detalles del agente
24
- const agentData = await agent.getAgentById({ id: agentId });
25
- logger_1.logger.info('\nDetalles del Agente:');
26
- logger_1.logger.doubleDivider();
27
- // Información básica
28
- logger_1.logger.info('Información Básica:');
29
- logger_1.logger.divider();
30
- logger_1.logger.info(`ID: ${agentData.id}`);
31
- logger_1.logger.info(`Nombre: ${agentData.name}`);
32
- logger_1.logger.info(`Descripción: ${agentData.description}`);
33
- logger_1.logger.info(`Estado: ${agentData.enable ? 'Activo' : 'Inactivo'}`);
34
- logger_1.logger.info(`Zona: ${agentData.zone}`);
35
- logger_1.logger.info(`Buffer: ${agentData.buffer}`);
36
- logger_1.logger.info(`Color: ${agentData.color}`);
37
- logger_1.logger.info(`Pregunta inicial: ${agentData.question}`);
38
- logger_1.logger.info(`Zona horaria: ${agentData.timezone}`);
39
- logger_1.logger.info(`Mostrar en chat: ${agentData.showInChat ? 'Sí' : 'No'}`);
33
+ // El SDK retorna { success, agent: {...} }
34
+ const response = await agent.getAgentById({ id: agentId });
35
+ const agentData = response.agent || response.data || response;
36
+ // Modo JSON crudo
37
+ if (options.raw) {
38
+ console.log(JSON.stringify(agentData, null, 2));
39
+ return;
40
+ }
41
+ const label = (key, value) => {
42
+ const v = value !== undefined && value !== null && value !== '' ? String(value) : chalk_1.default.gray('—');
43
+ console.log(` ${chalk_1.default.gray(key + ':')} ${chalk_1.default.white(v)}`);
44
+ };
45
+ const sectionHeader = (title) => {
46
+ console.log();
47
+ console.log(chalk_1.default.bold.hex('#4CAF50')(` ${title}`));
48
+ console.log(chalk_1.default.gray(' ' + '─'.repeat(40)));
49
+ };
50
+ console.log();
51
+ console.log(chalk_1.default.hex('#4CAF50')(' ┌' + '─'.repeat(58) + '┐'));
52
+ console.log(chalk_1.default.hex('#4CAF50')(' │') + chalk_1.default.bold(` ${agentData.name || 'Agente'}`).padEnd(68) + chalk_1.default.hex('#4CAF50')('│'));
53
+ console.log(chalk_1.default.hex('#4CAF50')(' │') + chalk_1.default.gray(` ${agentId}`).padEnd(68) + chalk_1.default.hex('#4CAF50')('│'));
54
+ console.log(chalk_1.default.hex('#4CAF50')(' └' + '─'.repeat(58) + '┘'));
55
+ // Informacion basica
56
+ sectionHeader('Informacion Basica');
57
+ label('ID', agentData.id);
58
+ label('Nombre', agentData.name);
59
+ label('Descripcion', agentData.description);
60
+ label('Estado', agentData.enable ? chalk_1.default.hex('#66BB6A')('Activo') : chalk_1.default.hex('#EF5350')('Inactivo'));
61
+ label('Zona', agentData.zone);
62
+ label('Buffer', agentData.buffer);
63
+ label('Color', agentData.color);
64
+ label('Pregunta inicial', agentData.question);
65
+ label('Zona horaria', agentData.timezone);
66
+ label('Mostrar en chat', agentData.showInChat ? 'Si' : 'No');
67
+ label('Tool Calling', agentData.useToolCalling ? 'Activado' : 'Desactivado');
40
68
  // Tags
41
69
  if (agentData.tags && agentData.tags.length > 0) {
42
- logger_1.logger.info(`\nTags: ${agentData.tags.join(', ')}`);
70
+ label('Tags', agentData.tags.join(', '));
71
+ }
72
+ // Prompt
73
+ if (agentData.prompt) {
74
+ sectionHeader('Prompt');
75
+ const promptPreview = agentData.prompt.length > 200
76
+ ? agentData.prompt.substring(0, 200) + '...'
77
+ : agentData.prompt;
78
+ console.log(chalk_1.default.white(' ' + promptPreview.split('\n').join('\n ')));
43
79
  }
44
80
  // Ejemplos
45
81
  if (agentData.examples && agentData.examples.length > 0) {
46
- logger_1.logger.info('\nEjemplos:');
47
- logger_1.logger.divider();
82
+ sectionHeader('Ejemplos');
48
83
  agentData.examples.forEach((example) => {
49
- logger_1.logger.info(`- ${example.value} (${example.color})`);
84
+ console.log(chalk_1.default.white(` - ${example.value || example}`));
50
85
  });
51
86
  }
52
87
  // Instrucciones
53
88
  if (agentData.instructions) {
54
- logger_1.logger.info('\nInstrucciones:');
55
- logger_1.logger.divider();
56
- const instructions = agentData.instructions;
57
- logger_1.logger.info(`Tono: ${instructions.tone}`);
58
- logger_1.logger.info(`Estilo: ${instructions.style}`);
59
- logger_1.logger.info(`Personalidad: ${instructions.personality}`);
60
- logger_1.logger.info(`Objetivo: ${instructions.objective}`);
61
- logger_1.logger.info(`Idioma: ${instructions.language}`);
62
- logger_1.logger.info(`Emojis: ${instructions.emojis ? 'Sí' : 'No'}`);
63
- logger_1.logger.info(`Formato preferido: ${instructions.preferredFormat}`);
64
- logger_1.logger.info(`Máximo de palabras: ${instructions.maxWords}`);
65
- if (instructions.avoidTopics) {
66
- logger_1.logger.info(`Temas a evitar: ${instructions.avoidTopics.join(', ')}`);
89
+ sectionHeader('Instrucciones');
90
+ const inst = agentData.instructions;
91
+ label('Tono', inst.tone);
92
+ label('Estilo', inst.style);
93
+ label('Personalidad', inst.personality);
94
+ label('Objetivo', inst.objective);
95
+ label('Idioma', inst.language);
96
+ label('Emojis', inst.emojis ? 'Si' : 'No');
97
+ label('Formato preferido', inst.preferredFormat);
98
+ label('Max palabras', inst.maxWords);
99
+ if (inst.avoidTopics && inst.avoidTopics.length > 0) {
100
+ label('Temas a evitar', inst.avoidTopics.join(', '));
67
101
  }
68
- logger_1.logger.info(`Responder solo si sabe: ${instructions.respondOnlyIfKnows ? '' : 'No'}`);
69
- logger_1.logger.info(`Mantener tono entre mensajes: ${instructions.maintainToneBetweenMessages ? '' : 'No'}`);
70
- logger_1.logger.info(`Saludo: ${instructions.greeting}`);
102
+ label('Solo responder si sabe', inst.respondOnlyIfKnows ? 'Si' : 'No');
103
+ label('Saludo', inst.greeting);
71
104
  }
72
105
  // Persona
73
106
  if (agentData.person) {
74
- logger_1.logger.info('\nPersona:');
75
- logger_1.logger.divider();
76
- const person = agentData.person;
77
- logger_1.logger.info(`Nombre: ${person.name}`);
78
- logger_1.logger.info(`Rol: ${person.role}`);
79
- logger_1.logger.info(`Habla en primera persona: ${person.speaksInFirstPerson ? '' : 'No'}`);
80
- logger_1.logger.info(`Es humano: ${person.isHuman ? 'Sí' : 'No'}`);
107
+ sectionHeader('Persona');
108
+ const p = agentData.person;
109
+ label('Nombre', p.name);
110
+ label('Rol', p.role);
111
+ label('Primera persona', p.speaksInFirstPerson ? 'Si' : 'No');
112
+ label('Es humano', p.isHuman ? 'Si' : 'No');
81
113
  }
82
114
  // Fallbacks
83
115
  if (agentData.fallbacks) {
84
- logger_1.logger.info('\nFallbacks:');
85
- logger_1.logger.divider();
86
- const fallbacks = agentData.fallbacks;
87
- logger_1.logger.info(`Sin respuesta: ${fallbacks.noAnswer}`);
88
- logger_1.logger.info(`Error de servicio: ${fallbacks.serviceError}`);
89
- logger_1.logger.info(`No entiende: ${fallbacks.doNotUnderstand}`);
116
+ sectionHeader('Fallbacks');
117
+ const fb = agentData.fallbacks;
118
+ label('Sin respuesta', fb.noAnswer);
119
+ label('Error de servicio', fb.serviceError);
120
+ label('No entiende', fb.doNotUnderstand);
90
121
  }
91
122
  // Reglas
92
123
  if (agentData.rules) {
93
- logger_1.logger.info('\nReglas:');
94
- logger_1.logger.divider();
95
- const rules = agentData.rules;
96
- logger_1.logger.info(`No mencionar precios: ${rules.doNotMentionPrices ? '' : 'No'}`);
97
- logger_1.logger.info(`No diagnosticar: ${rules.doNotDiagnose ? 'Sí' : 'No'}`);
98
- if (rules.doNotRespondOutsideHours) {
99
- logger_1.logger.info(`Horario de atención: ${rules.doNotRespondOutsideHours}`);
124
+ sectionHeader('Reglas');
125
+ const r = agentData.rules;
126
+ label('No mencionar precios', r.doNotMentionPrices ? 'Si' : 'No');
127
+ label('No diagnosticar', r.doNotDiagnose ? 'Si' : 'No');
128
+ if (r.doNotRespondOutsideHours) {
129
+ label('Fuera de horario', r.doNotRespondOutsideHours);
100
130
  }
101
131
  }
102
132
  // Servicios
103
133
  if (agentData.services && agentData.services.length > 0) {
104
- logger_1.logger.info('\nServicios:');
105
- logger_1.logger.divider();
106
- agentData.services.forEach((service, index) => {
107
- logger_1.logger.info(`\nServicio ${index + 1}:`);
108
- logger_1.logger.info(`Intent: ${service.intent}`);
109
- logger_1.logger.info(`Referencia: ${service.reference}`);
110
- logger_1.logger.info(`Habilitado: ${service.enabled ? '' : 'No'}`);
111
- logger_1.logger.info(`Método: ${service.method}`);
112
- logger_1.logger.info(`Endpoint: ${service.endpoint}`);
113
- if (service.requiredFields) {
114
- logger_1.logger.info('Campos requeridos:');
115
- service.requiredFields.forEach((field) => {
116
- logger_1.logger.info(` - ${field.name} (${field.type}): ${field.description}`);
134
+ sectionHeader(`Servicios (${agentData.services.length})`);
135
+ agentData.services.forEach((svc, i) => {
136
+ console.log(chalk_1.default.hex('#2196F3')(` ${i + 1}. ${svc.intent || svc.reference || 'Servicio'}`));
137
+ label(' Referencia', svc.reference);
138
+ label(' Habilitado', svc.enabled ? 'Si' : 'No');
139
+ label(' Metodo', svc.method);
140
+ label(' Endpoint', svc.endpoint);
141
+ if (svc.requiredFields && svc.requiredFields.length > 0) {
142
+ console.log(chalk_1.default.gray(' Campos:'));
143
+ svc.requiredFields.forEach((f) => {
144
+ console.log(chalk_1.default.white(` - ${f.name} (${f.type}): ${f.description || ''}`));
117
145
  });
118
146
  }
119
147
  });
120
148
  }
121
149
  // Acciones
122
150
  if (agentData.actions && agentData.actions.length > 0) {
123
- logger_1.logger.info('\nAcciones:');
124
- logger_1.logger.divider();
125
- agentData.actions.forEach((action, index) => {
126
- logger_1.logger.info(`\nAcción ${index + 1}:`);
127
- logger_1.logger.info(`Intent: ${action.intent}`);
128
- logger_1.logger.info(`Referencia: ${action.reference}`);
129
- logger_1.logger.info(`Habilitado: ${action.enabled ? 'Sí' : 'No'}`);
130
- if (action.tags) {
131
- logger_1.logger.info(`Tags: ${action.tags.join(', ')}`);
151
+ sectionHeader(`Acciones (${agentData.actions.length})`);
152
+ agentData.actions.forEach((act, i) => {
153
+ console.log(chalk_1.default.hex('#FFA726')(` ${i + 1}. ${act.intent || act.reference || 'Accion'}`));
154
+ label(' Referencia', act.reference);
155
+ label(' Habilitado', act.enabled ? 'Si' : 'No');
156
+ if (act.tags && act.tags.length > 0) {
157
+ label(' Tags', act.tags.join(', '));
132
158
  }
133
- logger_1.logger.info(`Mensaje de respuesta: ${action.responseMessage}`);
134
- if (action.action) {
135
- logger_1.logger.info('Sub-acciones:');
136
- action.action.forEach((subAction) => {
137
- logger_1.logger.info(` - ${subAction.type}: ${subAction.value}`);
159
+ label(' Respuesta', act.responseMessage);
160
+ if (act.action && act.action.length > 0) {
161
+ console.log(chalk_1.default.gray(' Sub-acciones:'));
162
+ act.action.forEach((sa) => {
163
+ console.log(chalk_1.default.white(` - ${sa.type}: ${sa.value || ''}`));
138
164
  });
139
165
  }
140
166
  });
141
167
  }
142
168
  // Canales
143
169
  if (agentData.channels && agentData.channels.length > 0) {
144
- logger_1.logger.info('\nCanales:');
145
- logger_1.logger.divider();
146
- agentData.channels.forEach((channel) => {
147
- logger_1.logger.info(`${channel.channel}: ${channel.key}`);
170
+ sectionHeader(`Canales (${agentData.channels.length})`);
171
+ agentData.channels.forEach((ch) => {
172
+ console.log(chalk_1.default.white(` ${chalk_1.default.hex('#22d3ee')(ch.channel || 'canal')}: ${ch.key || ''}`));
173
+ });
174
+ }
175
+ // AI Config
176
+ if (agentData.customAIConfig && agentData.aiProviders && agentData.aiProviders.length > 0) {
177
+ sectionHeader('Modelo IA');
178
+ agentData.aiProviders.forEach((ai) => {
179
+ label('Proveedor', ai.provider);
180
+ label('Modelo', ai.model);
148
181
  });
149
182
  }
150
- logger_1.logger.doubleDivider();
183
+ console.log();
184
+ console.log(chalk_1.default.gray(' Usa --raw para ver el JSON completo'));
185
+ console.log();
151
186
  if (options.dev) {
152
187
  logger_1.logger.warning('Ambiente: desarrollo');
153
188
  }
@@ -104,6 +104,7 @@ function printHeader(agentName) {
104
104
  const COMMANDS_HELP = `
105
105
  ${chalk_1.default.bold('Comandos disponibles:')}
106
106
  ${chalk_1.default.hex('#4CAF50')('/filter <tipo>')} Toggle filtro por tipo (ej: msg_in, error, tool_call)
107
+ ${chalk_1.default.hex('#4CAF50')('/filter clear')} Quitar todos los filtros
107
108
  ${chalk_1.default.hex('#4CAF50')('/filters')} Mostrar filtros activos
108
109
  ${chalk_1.default.hex('#4CAF50')('/clear')} Limpiar pantalla
109
110
  ${chalk_1.default.hex('#4CAF50')('/json')} Toggle modo JSON expandido
@@ -118,11 +119,18 @@ exports.monitorCommand = new commander_1.Command('monitor')
118
119
  .option('-f, --filter <types>', 'Filtrar por tipos separados por coma (ej: msg_in,msg_out)')
119
120
  .option('--no-session', 'Ocultar session ID')
120
121
  .option('--json', 'Modo JSON crudo (para piping)', false)
122
+ .option('-w, --workspace <id>', 'Workspace ID (sobreescribe config local)')
123
+ .option('-z, --zone <zone>', 'Zona LA o EU (sobreescribe config local)')
121
124
  .option('--dev', 'Usar ambiente de desarrollo', false)
122
125
  .action(async (options) => {
123
126
  try {
124
127
  const credentials = await (0, credentials_1.getStoredCredentials)();
125
- const baseUrl = getBaseUrl(credentials.zone, options.dev);
128
+ const effectiveWorkspace = options.workspace || credentials.workspace;
129
+ const effectiveZone = (options.zone?.toUpperCase() === 'EU' ? 'EU' : options.zone?.toUpperCase() === 'LA' ? 'LA' : credentials.zone);
130
+ const baseUrl = getBaseUrl(effectiveZone, options.dev);
131
+ if (options.workspace || options.zone) {
132
+ console.log(chalk_1.default.hex('#FFA726')(`\n Modo soporte: workspace=${effectiveWorkspace} zona=${effectiveZone}`));
133
+ }
126
134
  // Filtros iniciales
127
135
  const activeFilters = new Set(options.filter ? options.filter.split(',').map(f => f.trim()) : []);
128
136
  let jsonMode = options.json;
@@ -132,9 +140,9 @@ exports.monitorCommand = new commander_1.Command('monitor')
132
140
  try {
133
141
  process.stdout.write(chalk_1.default.gray(' Conectando con agente...'));
134
142
  const agent = new plazbot_1.Agent({
135
- workspaceId: credentials.workspace,
143
+ workspaceId: effectiveWorkspace,
136
144
  apiKey: credentials.apiKey,
137
- zone: credentials.zone,
145
+ zone: effectiveZone,
138
146
  ...(options.dev && { customUrl: 'http://localhost:5090' }),
139
147
  });
140
148
  const info = await agent.getAgentById({ id: options.agentId });
@@ -236,7 +244,12 @@ exports.monitorCommand = new commander_1.Command('monitor')
236
244
  if (cmd.startsWith('/filter ')) {
237
245
  const filterType = cmd.substring(8).trim();
238
246
  if (!filterType) {
239
- console.log(chalk_1.default.gray(' Uso: /filter <tipo>'));
247
+ console.log(chalk_1.default.gray(' Uso: /filter <tipo> | /filter clear'));
248
+ return;
249
+ }
250
+ if (filterType === 'clear') {
251
+ activeFilters.clear();
252
+ console.log(chalk_1.default.gray(' Todos los filtros removidos (mostrando todos)'));
240
253
  return;
241
254
  }
242
255
  if (activeFilters.has(filterType)) {
@@ -5,14 +5,170 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runAgentWizard = runAgentWizard;
7
7
  const inquirer_1 = __importDefault(require("inquirer"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const axios_1 = __importDefault(require("axios"));
10
+ const ora_1 = __importDefault(require("ora"));
8
11
  const ui_1 = require("../../utils/ui");
9
12
  const MODELS = {
10
- openai: ['gpt-4o', 'gpt-4', 'gpt-3.5-turbo', 'o1-preview', 'o1-mini'],
13
+ openai: ['gpt-4o', 'gpt-4o-mini', 'gpt-4', 'gpt-3.5-turbo', 'o1-preview', 'o1-mini'],
11
14
  claude: ['claude-3-5-sonnet-20241022', 'claude-3-opus-20240229', 'claude-3-haiku-20240307'],
12
15
  gemini: ['gemini-2.0-flash', 'gemini-1.5-pro', 'gemini-1.5-flash'],
13
16
  };
14
17
  const COLORS = ['blue', 'green', 'orange', 'gray', 'white'];
15
- async function runAgentWizard(zone) {
18
+ function getBaseUrl(zone, dev) {
19
+ if (dev)
20
+ return 'http://localhost:5090';
21
+ return zone === 'EU' ? 'https://apieu.plazbot.com' : 'https://api.plazbot.com';
22
+ }
23
+ const CHANNEL_CHOICES = [
24
+ { name: 'WhatsApp', value: 'whatsapp' },
25
+ { name: 'WhatsApp Business', value: 'whatsapp_business' },
26
+ { name: 'Instagram', value: 'instagram' },
27
+ { name: 'Messenger', value: 'facebook' },
28
+ ];
29
+ const CHANNEL_AGENT_MAP = {
30
+ whatsapp: 'whatsapp',
31
+ whatsapp_business: 'whatsapp',
32
+ instagram: 'instagram',
33
+ facebook: 'facebook',
34
+ };
35
+ async function connectChannelFlow(ctx) {
36
+ const channels = [];
37
+ const baseUrl = getBaseUrl(ctx.zone, ctx.dev);
38
+ const headers = {
39
+ 'Authorization': `Bearer ${ctx.apiKey}`,
40
+ 'x-workspace-id': ctx.workspaceId,
41
+ 'Content-Type': 'application/json',
42
+ };
43
+ let addMore = true;
44
+ while (addMore) {
45
+ const { channelType } = await inquirer_1.default.prompt([{
46
+ type: 'list',
47
+ name: 'channelType',
48
+ message: 'Que canal deseas conectar?',
49
+ choices: CHANNEL_CHOICES,
50
+ }]);
51
+ const channelLabel = CHANNEL_CHOICES.find(c => c.value === channelType)?.name || channelType;
52
+ // Generar link de onboarding
53
+ const spinner = (0, ora_1.default)({ text: chalk_1.default.gray('Generando link de conexion...'), spinner: 'dots', color: 'green' }).start();
54
+ try {
55
+ const linkRes = await axios_1.default.post(`${baseUrl}/api/workspace/${ctx.workspaceId}/onboarding-link`, { type: channelType }, { headers });
56
+ if (!linkRes.data?.success) {
57
+ spinner.fail(chalk_1.default.hex('#EF5350')(`Error: ${linkRes.data?.message || 'No se pudo generar el link'}`));
58
+ const { tryManual } = await inquirer_1.default.prompt([{
59
+ type: 'confirm', name: 'tryManual',
60
+ message: 'Deseas ingresar el numero manualmente?', default: false,
61
+ }]);
62
+ if (tryManual) {
63
+ const { key } = await inquirer_1.default.prompt([
64
+ { type: 'input', name: 'key', message: `ID/numero del canal ${channelLabel}:`, validate: (v) => v.length > 0 || 'Requerido' },
65
+ ]);
66
+ channels.push({ channel: CHANNEL_AGENT_MAP[channelType], key, multianswer: false });
67
+ }
68
+ const { more } = await inquirer_1.default.prompt([{ type: 'confirm', name: 'more', message: 'Conectar otro canal?', default: false }]);
69
+ addMore = more;
70
+ continue;
71
+ }
72
+ const shortUrl = linkRes.data.data?.shortUrl || '';
73
+ spinner.stop();
74
+ // Mostrar link
75
+ console.log();
76
+ console.log(chalk_1.default.hex('#4CAF50')(' ─'.repeat(32)));
77
+ console.log(chalk_1.default.bold(` Envia este link para conectar ${channelLabel}:`));
78
+ console.log();
79
+ console.log(' ' + chalk_1.default.hex('#22d3ee')(shortUrl));
80
+ console.log();
81
+ console.log(chalk_1.default.hex('#4CAF50')(' ─'.repeat(32)));
82
+ console.log();
83
+ // Snapshot de integraciones actuales
84
+ let prevIntegrationIds = new Set();
85
+ try {
86
+ const wkRes = await axios_1.default.get(`${baseUrl}/api/workspace/${ctx.workspaceId}`, { headers });
87
+ const integrations = wkRes.data?.integrations || [];
88
+ prevIntegrationIds = new Set(integrations.map((i) => i.id));
89
+ }
90
+ catch {
91
+ // Si falla, se parte de vacio
92
+ }
93
+ // Polling para detectar nueva integracion
94
+ const pollSpinner = (0, ora_1.default)({ text: chalk_1.default.gray('Esperando conexion... (Ctrl+C para cancelar)'), spinner: 'dots', color: 'cyan' }).start();
95
+ const POLL_INTERVAL = 5000;
96
+ const MAX_POLLS = 60; // 5 minutos
97
+ let detected = null;
98
+ for (let i = 0; i < MAX_POLLS; i++) {
99
+ await new Promise(r => setTimeout(r, POLL_INTERVAL));
100
+ try {
101
+ const wkRes = await axios_1.default.get(`${baseUrl}/api/workspace/${ctx.workspaceId}`, { headers });
102
+ const integrations = wkRes.data?.integrations || [];
103
+ // Buscar integraciones nuevas (cualquier ID que no existia antes)
104
+ const newOnes = integrations.filter((ig) => !prevIntegrationIds.has(ig.id));
105
+ if (newOnes.length > 0) {
106
+ // Preferir la que coincida con el tipo solicitado
107
+ const targetType = channelType === 'whatsapp_business' ? 'whatsapp' : channelType;
108
+ const matchingNew = newOnes.find((ig) => ig.type === targetType);
109
+ detected = matchingNew || newOnes[0];
110
+ break;
111
+ }
112
+ }
113
+ catch {
114
+ // Ignorar errores de poll individual
115
+ }
116
+ const elapsed = Math.floor(((i + 1) * POLL_INTERVAL) / 1000);
117
+ pollSpinner.text = chalk_1.default.gray(`Esperando conexion... ${elapsed}s`);
118
+ }
119
+ if (detected) {
120
+ const detectedName = detected.aliasName || detected.cellphoneNumberFormat || detected.cellphoneNumber || detected.srcName || detected.id;
121
+ pollSpinner.succeed(chalk_1.default.hex('#4ade80')(`Conexion detectada: ${detectedName} (${channelLabel})`));
122
+ const { associate } = await inquirer_1.default.prompt([{
123
+ type: 'confirm', name: 'associate',
124
+ message: 'Deseas asociar este canal al agente?', default: true,
125
+ }]);
126
+ if (associate) {
127
+ channels.push({
128
+ channel: CHANNEL_AGENT_MAP[channelType],
129
+ key: detected.id,
130
+ multianswer: false,
131
+ });
132
+ console.log(chalk_1.default.hex('#4ade80')(' Canal asociado correctamente.'));
133
+ }
134
+ }
135
+ else {
136
+ pollSpinner.warn(chalk_1.default.hex('#FFA726')('Timeout: no se detecto una conexion.'));
137
+ const { action } = await inquirer_1.default.prompt([{
138
+ type: 'list', name: 'action',
139
+ message: 'Que deseas hacer?',
140
+ choices: [
141
+ { name: 'Ingresar numero/ID manualmente', value: 'manual' },
142
+ { name: 'Saltar este paso', value: 'skip' },
143
+ ],
144
+ }]);
145
+ if (action === 'manual') {
146
+ const { key } = await inquirer_1.default.prompt([
147
+ { type: 'input', name: 'key', message: `ID/numero del canal ${channelLabel}:`, validate: (v) => v.length > 0 || 'Requerido' },
148
+ ]);
149
+ channels.push({ channel: CHANNEL_AGENT_MAP[channelType], key, multianswer: false });
150
+ }
151
+ }
152
+ }
153
+ catch (err) {
154
+ spinner.fail(chalk_1.default.hex('#EF5350')(`Error al generar link: ${err instanceof Error ? err.message : err}`));
155
+ const { tryManual } = await inquirer_1.default.prompt([{
156
+ type: 'confirm', name: 'tryManual',
157
+ message: 'Deseas ingresar el numero manualmente?', default: false,
158
+ }]);
159
+ if (tryManual) {
160
+ const { key } = await inquirer_1.default.prompt([
161
+ { type: 'input', name: 'key', message: `ID/numero del canal ${channelLabel}:`, validate: (v) => v.length > 0 || 'Requerido' },
162
+ ]);
163
+ channels.push({ channel: CHANNEL_AGENT_MAP[channelType], key, multianswer: false });
164
+ }
165
+ }
166
+ const { more } = await inquirer_1.default.prompt([{ type: 'confirm', name: 'more', message: 'Conectar otro canal?', default: false }]);
167
+ addMore = more;
168
+ }
169
+ return channels;
170
+ }
171
+ async function runAgentWizard(zone, workspaceId, apiKey, dev) {
16
172
  console.log((0, ui_1.section)('Crear nuevo agente de IA'));
17
173
  console.log(ui_1.theme.muted(' Responde las siguientes preguntas para configurar tu agente.\n'));
18
174
  // Paso 1: Informacion basica
@@ -271,28 +427,30 @@ async function runAgentWizard(zone) {
271
427
  const aiConfig = await inquirer_1.default.prompt([
272
428
  { type: 'list', name: 'model', message: 'Modelo:', choices: models },
273
429
  { type: 'password', name: 'apiToken', message: 'API Token:', mask: '*', validate: (v) => v.length > 0 || 'Requerido' },
274
- { type: 'number', name: 'temperature', message: 'Temperatura (0-2):', default: 0.7 },
275
- { type: 'number', name: 'maxTokens', message: 'Max tokens (1024-16384):', default: 4096 },
276
430
  ]);
277
431
  aiProviders.push({
278
432
  provider: ai.provider,
279
433
  model: aiConfig.model,
280
434
  apiToken: aiConfig.apiToken,
281
- temperature: aiConfig.temperature,
282
- maxTokens: aiConfig.maxTokens,
435
+ temperature: 0.7,
436
+ maxTokens: 4096,
283
437
  isDefault: true,
284
438
  });
285
439
  }
286
- // Paso 7: Canal WhatsApp
287
- console.log(ui_1.theme.bold('\n Paso 7/8: Canal WhatsApp'));
288
- const channels = [];
289
- const { addWhatsApp } = await inquirer_1.default.prompt([{
440
+ // Paso 7: Conectar canal
441
+ console.log(ui_1.theme.bold('\n Paso 7/8: Conectar canal'));
442
+ let channels = [];
443
+ const { addChannel } = await inquirer_1.default.prompt([{
290
444
  type: 'confirm',
291
- name: 'addWhatsApp',
292
- message: 'Conectar a un numero de WhatsApp?',
445
+ name: 'addChannel',
446
+ message: 'Deseas conectar un canal (WhatsApp, Instagram, Messenger)?',
293
447
  default: false,
294
448
  }]);
295
- if (addWhatsApp) {
449
+ if (addChannel && workspaceId && apiKey) {
450
+ channels = await connectChannelFlow({ zone, workspaceId, apiKey, dev: dev || false });
451
+ }
452
+ else if (addChannel) {
453
+ // Fallback manual si no hay credenciales para API
296
454
  const wa = await inquirer_1.default.prompt([
297
455
  { type: 'input', name: 'key', message: 'Numero de WhatsApp (con codigo de pais):', validate: (v) => v.length > 0 || 'Requerido' },
298
456
  { type: 'confirm', name: 'multianswer', message: 'Permitir multiples respuestas?', default: false },
@@ -363,7 +521,14 @@ async function runAgentWizard(zone) {
363
521
  console.log((0, ui_1.kvPair)('Servicios', String(config.services.length)));
364
522
  console.log((0, ui_1.kvPair)('Acciones', String(config.actions.length)));
365
523
  console.log((0, ui_1.kvPair)('AI Provider', config.customAIConfig ? config.aiProviders[0]?.provider + ' / ' + config.aiProviders[0]?.model : 'Default (Plazbot)'));
366
- console.log((0, ui_1.kvPair)('Canal WhatsApp', config.channels.length > 0 ? config.channels[0].key : 'No configurado'));
524
+ if (config.channels.length > 0) {
525
+ config.channels.forEach((ch, i) => {
526
+ console.log((0, ui_1.kvPair)(`Canal ${i + 1} (${ch.channel})`, ch.key));
527
+ });
528
+ }
529
+ else {
530
+ console.log((0, ui_1.kvPair)('Canales', 'No configurado'));
531
+ }
367
532
  console.log();
368
533
  return config;
369
534
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plazbot-cli",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "CLI para Plazbot SDK",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -37,7 +37,7 @@ export const createCommand = new Command('create')
37
37
  logger.json(agentConfig);
38
38
  } else {
39
39
  // Modo wizard interactivo
40
- agentConfig = await runAgentWizard(credentials.zone);
40
+ agentConfig = await runAgentWizard(credentials.zone, credentials.workspace, credentials.apiKey, options.dev);
41
41
 
42
42
  const inquirer = await import('inquirer');
43
43
  const { confirm } = await inquirer.default.prompt([{
@@ -70,7 +70,9 @@ export const createCommand = new Command('create')
70
70
  logger.label('Tool Calling', agentConfig.useToolCalling ? 'Activado' : 'Desactivado');
71
71
 
72
72
  if (agentConfig.channels && agentConfig.channels.length > 0) {
73
- logger.label('WhatsApp', agentConfig.channels[0].key);
73
+ agentConfig.channels.forEach((ch: any) => {
74
+ logger.label(`Canal (${ch.channel})`, ch.key);
75
+ });
74
76
  }
75
77
 
76
78
  console.log();
@@ -3,127 +3,158 @@ import { Agent } from 'plazbot';
3
3
  import { getStoredCredentials } from '../../utils/credentials';
4
4
  import { logger } from '../../utils/logger';
5
5
  import { AgentCommandOptions } from '../../types/agent';
6
+ import chalk from 'chalk';
6
7
 
7
8
  export const getCommand = new Command('get')
8
9
  .description('Obtiene información detallada de un agente específico')
9
10
  .argument('<agentId>', 'ID del agente a consultar')
11
+ .option('-w, --workspace <id>', 'Workspace ID (sobreescribe config local)')
12
+ .option('-z, --zone <zone>', 'Zona LA o EU (sobreescribe config local)')
13
+ .option('--raw', 'Mostrar el JSON completo sin formatear', false)
10
14
  .option('--dev', 'Usar ambiente de desarrollo', false)
11
- .action(async (agentId: string, options: AgentCommandOptions) => {
15
+ .action(async (agentId: string, options: AgentCommandOptions & { raw: boolean; workspace?: string; zone?: string }) => {
12
16
  try {
13
- // Obtener credenciales guardadas
14
17
  const credentials = await getStoredCredentials();
15
-
16
- // Crear instancia del agente con las credenciales guardadas
18
+
19
+ const effectiveWorkspace = options.workspace || credentials.workspace;
20
+ const effectiveZone = (options.zone?.toUpperCase() === 'EU' ? 'EU' : options.zone?.toUpperCase() === 'LA' ? 'LA' : credentials.zone) as 'LA' | 'EU';
21
+
22
+ if (options.workspace || options.zone) {
23
+ console.log(chalk.hex('#FFA726')(`\n Modo soporte: workspace=${effectiveWorkspace} zona=${effectiveZone}`));
24
+ }
25
+
17
26
  const agent = new Agent({
18
- workspaceId: credentials.workspace,
27
+ workspaceId: effectiveWorkspace,
19
28
  apiKey: credentials.apiKey,
20
- zone: credentials.zone,
29
+ zone: effectiveZone,
21
30
  ...(options.dev && { customUrl: "http://localhost:5090" })
22
31
  });
23
32
 
24
- // Obtener detalles del agente
25
- const agentData = await agent.getAgentById({ id: agentId });
26
-
27
- logger.info('\nDetalles del Agente:');
28
- logger.doubleDivider();
29
-
30
- // Información básica
31
- logger.info('Información Básica:');
32
- logger.divider();
33
- logger.info(`ID: ${agentData.id}`);
34
- logger.info(`Nombre: ${agentData.name}`);
35
- logger.info(`Descripción: ${agentData.description}`);
36
- logger.info(`Estado: ${agentData.enable ? 'Activo' : 'Inactivo'}`);
37
- logger.info(`Zona: ${agentData.zone}`);
38
- logger.info(`Buffer: ${agentData.buffer}`);
39
- logger.info(`Color: ${agentData.color}`);
40
- logger.info(`Pregunta inicial: ${agentData.question}`);
41
- logger.info(`Zona horaria: ${agentData.timezone}`);
42
- logger.info(`Mostrar en chat: ${agentData.showInChat ? '' : 'No'}`);
43
-
33
+ // El SDK retorna { success, agent: {...} }
34
+ const response: any = await agent.getAgentById({ id: agentId });
35
+ const agentData = response.agent || response.data || response;
36
+
37
+ // Modo JSON crudo
38
+ if (options.raw) {
39
+ console.log(JSON.stringify(agentData, null, 2));
40
+ return;
41
+ }
42
+
43
+ const label = (key: string, value: any) => {
44
+ const v = value !== undefined && value !== null && value !== '' ? String(value) : chalk.gray('—');
45
+ console.log(` ${chalk.gray(key + ':')} ${chalk.white(v)}`);
46
+ };
47
+
48
+ const sectionHeader = (title: string) => {
49
+ console.log();
50
+ console.log(chalk.bold.hex('#4CAF50')(` ${title}`));
51
+ console.log(chalk.gray(' ' + ''.repeat(40)));
52
+ };
53
+
54
+ console.log();
55
+ console.log(chalk.hex('#4CAF50')(' ┌' + '─'.repeat(58) + '┐'));
56
+ console.log(chalk.hex('#4CAF50')(' │') + chalk.bold(` ${agentData.name || 'Agente'}`).padEnd(68) + chalk.hex('#4CAF50')('│'));
57
+ console.log(chalk.hex('#4CAF50')(' │') + chalk.gray(` ${agentId}`).padEnd(68) + chalk.hex('#4CAF50')('│'));
58
+ console.log(chalk.hex('#4CAF50')(' └' + '─'.repeat(58) + '┘'));
59
+
60
+ // Informacion basica
61
+ sectionHeader('Informacion Basica');
62
+ label('ID', agentData.id);
63
+ label('Nombre', agentData.name);
64
+ label('Descripcion', agentData.description);
65
+ label('Estado', agentData.enable ? chalk.hex('#66BB6A')('Activo') : chalk.hex('#EF5350')('Inactivo'));
66
+ label('Zona', agentData.zone);
67
+ label('Buffer', agentData.buffer);
68
+ label('Color', agentData.color);
69
+ label('Pregunta inicial', agentData.question);
70
+ label('Zona horaria', agentData.timezone);
71
+ label('Mostrar en chat', agentData.showInChat ? 'Si' : 'No');
72
+ label('Tool Calling', agentData.useToolCalling ? 'Activado' : 'Desactivado');
73
+
44
74
  // Tags
45
75
  if (agentData.tags && agentData.tags.length > 0) {
46
- logger.info(`\nTags: ${agentData.tags.join(', ')}`);
76
+ label('Tags', agentData.tags.join(', '));
77
+ }
78
+
79
+ // Prompt
80
+ if (agentData.prompt) {
81
+ sectionHeader('Prompt');
82
+ const promptPreview = agentData.prompt.length > 200
83
+ ? agentData.prompt.substring(0, 200) + '...'
84
+ : agentData.prompt;
85
+ console.log(chalk.white(' ' + promptPreview.split('\n').join('\n ')));
47
86
  }
48
87
 
49
88
  // Ejemplos
50
89
  if (agentData.examples && agentData.examples.length > 0) {
51
- logger.info('\nEjemplos:');
52
- logger.divider();
53
- agentData.examples.forEach((example: { value: string; color: string }) => {
54
- logger.info(`- ${example.value} (${example.color})`);
90
+ sectionHeader('Ejemplos');
91
+ agentData.examples.forEach((example: any) => {
92
+ console.log(chalk.white(` - ${example.value || example}`));
55
93
  });
56
94
  }
57
95
 
58
96
  // Instrucciones
59
97
  if (agentData.instructions) {
60
- logger.info('\nInstrucciones:');
61
- logger.divider();
62
- const instructions = agentData.instructions;
63
- logger.info(`Tono: ${instructions.tone}`);
64
- logger.info(`Estilo: ${instructions.style}`);
65
- logger.info(`Personalidad: ${instructions.personality}`);
66
- logger.info(`Objetivo: ${instructions.objective}`);
67
- logger.info(`Idioma: ${instructions.language}`);
68
- logger.info(`Emojis: ${instructions.emojis ? 'Sí' : 'No'}`);
69
- logger.info(`Formato preferido: ${instructions.preferredFormat}`);
70
- logger.info(`Máximo de palabras: ${instructions.maxWords}`);
71
- if (instructions.avoidTopics) {
72
- logger.info(`Temas a evitar: ${instructions.avoidTopics.join(', ')}`);
98
+ sectionHeader('Instrucciones');
99
+ const inst = agentData.instructions;
100
+ label('Tono', inst.tone);
101
+ label('Estilo', inst.style);
102
+ label('Personalidad', inst.personality);
103
+ label('Objetivo', inst.objective);
104
+ label('Idioma', inst.language);
105
+ label('Emojis', inst.emojis ? 'Si' : 'No');
106
+ label('Formato preferido', inst.preferredFormat);
107
+ label('Max palabras', inst.maxWords);
108
+ if (inst.avoidTopics && inst.avoidTopics.length > 0) {
109
+ label('Temas a evitar', inst.avoidTopics.join(', '));
73
110
  }
74
- logger.info(`Responder solo si sabe: ${instructions.respondOnlyIfKnows ? '' : 'No'}`);
75
- logger.info(`Mantener tono entre mensajes: ${instructions.maintainToneBetweenMessages ? '' : 'No'}`);
76
- logger.info(`Saludo: ${instructions.greeting}`);
111
+ label('Solo responder si sabe', inst.respondOnlyIfKnows ? 'Si' : 'No');
112
+ label('Saludo', inst.greeting);
77
113
  }
78
114
 
79
115
  // Persona
80
116
  if (agentData.person) {
81
- logger.info('\nPersona:');
82
- logger.divider();
83
- const person = agentData.person;
84
- logger.info(`Nombre: ${person.name}`);
85
- logger.info(`Rol: ${person.role}`);
86
- logger.info(`Habla en primera persona: ${person.speaksInFirstPerson ? '' : 'No'}`);
87
- logger.info(`Es humano: ${person.isHuman ? 'Sí' : 'No'}`);
117
+ sectionHeader('Persona');
118
+ const p = agentData.person;
119
+ label('Nombre', p.name);
120
+ label('Rol', p.role);
121
+ label('Primera persona', p.speaksInFirstPerson ? 'Si' : 'No');
122
+ label('Es humano', p.isHuman ? 'Si' : 'No');
88
123
  }
89
124
 
90
125
  // Fallbacks
91
126
  if (agentData.fallbacks) {
92
- logger.info('\nFallbacks:');
93
- logger.divider();
94
- const fallbacks = agentData.fallbacks;
95
- logger.info(`Sin respuesta: ${fallbacks.noAnswer}`);
96
- logger.info(`Error de servicio: ${fallbacks.serviceError}`);
97
- logger.info(`No entiende: ${fallbacks.doNotUnderstand}`);
127
+ sectionHeader('Fallbacks');
128
+ const fb = agentData.fallbacks;
129
+ label('Sin respuesta', fb.noAnswer);
130
+ label('Error de servicio', fb.serviceError);
131
+ label('No entiende', fb.doNotUnderstand);
98
132
  }
99
133
 
100
134
  // Reglas
101
135
  if (agentData.rules) {
102
- logger.info('\nReglas:');
103
- logger.divider();
104
- const rules = agentData.rules;
105
- logger.info(`No mencionar precios: ${rules.doNotMentionPrices ? '' : 'No'}`);
106
- logger.info(`No diagnosticar: ${rules.doNotDiagnose ? 'Sí' : 'No'}`);
107
- if (rules.doNotRespondOutsideHours) {
108
- logger.info(`Horario de atención: ${rules.doNotRespondOutsideHours}`);
136
+ sectionHeader('Reglas');
137
+ const r = agentData.rules;
138
+ label('No mencionar precios', r.doNotMentionPrices ? 'Si' : 'No');
139
+ label('No diagnosticar', r.doNotDiagnose ? 'Si' : 'No');
140
+ if (r.doNotRespondOutsideHours) {
141
+ label('Fuera de horario', r.doNotRespondOutsideHours);
109
142
  }
110
143
  }
111
144
 
112
145
  // Servicios
113
146
  if (agentData.services && agentData.services.length > 0) {
114
- logger.info('\nServicios:');
115
- logger.divider();
116
- agentData.services.forEach((service: any, index: number) => {
117
- logger.info(`\nServicio ${index + 1}:`);
118
- logger.info(`Intent: ${service.intent}`);
119
- logger.info(`Referencia: ${service.reference}`);
120
- logger.info(`Habilitado: ${service.enabled ? '' : 'No'}`);
121
- logger.info(`Método: ${service.method}`);
122
- logger.info(`Endpoint: ${service.endpoint}`);
123
- if (service.requiredFields) {
124
- logger.info('Campos requeridos:');
125
- service.requiredFields.forEach((field: any) => {
126
- logger.info(` - ${field.name} (${field.type}): ${field.description}`);
147
+ sectionHeader(`Servicios (${agentData.services.length})`);
148
+ agentData.services.forEach((svc: any, i: number) => {
149
+ console.log(chalk.hex('#2196F3')(` ${i + 1}. ${svc.intent || svc.reference || 'Servicio'}`));
150
+ label(' Referencia', svc.reference);
151
+ label(' Habilitado', svc.enabled ? 'Si' : 'No');
152
+ label(' Metodo', svc.method);
153
+ label(' Endpoint', svc.endpoint);
154
+ if (svc.requiredFields && svc.requiredFields.length > 0) {
155
+ console.log(chalk.gray(' Campos:'));
156
+ svc.requiredFields.forEach((f: any) => {
157
+ console.log(chalk.white(` - ${f.name} (${f.type}): ${f.description || ''}`));
127
158
  });
128
159
  }
129
160
  });
@@ -131,21 +162,19 @@ export const getCommand = new Command('get')
131
162
 
132
163
  // Acciones
133
164
  if (agentData.actions && agentData.actions.length > 0) {
134
- logger.info('\nAcciones:');
135
- logger.divider();
136
- agentData.actions.forEach((action: any, index: number) => {
137
- logger.info(`\nAcción ${index + 1}:`);
138
- logger.info(`Intent: ${action.intent}`);
139
- logger.info(`Referencia: ${action.reference}`);
140
- logger.info(`Habilitado: ${action.enabled ? 'Sí' : 'No'}`);
141
- if (action.tags) {
142
- logger.info(`Tags: ${action.tags.join(', ')}`);
165
+ sectionHeader(`Acciones (${agentData.actions.length})`);
166
+ agentData.actions.forEach((act: any, i: number) => {
167
+ console.log(chalk.hex('#FFA726')(` ${i + 1}. ${act.intent || act.reference || 'Accion'}`));
168
+ label(' Referencia', act.reference);
169
+ label(' Habilitado', act.enabled ? 'Si' : 'No');
170
+ if (act.tags && act.tags.length > 0) {
171
+ label(' Tags', act.tags.join(', '));
143
172
  }
144
- logger.info(`Mensaje de respuesta: ${action.responseMessage}`);
145
- if (action.action) {
146
- logger.info('Sub-acciones:');
147
- action.action.forEach((subAction: any) => {
148
- logger.info(` - ${subAction.type}: ${subAction.value}`);
173
+ label(' Respuesta', act.responseMessage);
174
+ if (act.action && act.action.length > 0) {
175
+ console.log(chalk.gray(' Sub-acciones:'));
176
+ act.action.forEach((sa: any) => {
177
+ console.log(chalk.white(` - ${sa.type}: ${sa.value || ''}`));
149
178
  });
150
179
  }
151
180
  });
@@ -153,19 +182,29 @@ export const getCommand = new Command('get')
153
182
 
154
183
  // Canales
155
184
  if (agentData.channels && agentData.channels.length > 0) {
156
- logger.info('\nCanales:');
157
- logger.divider();
158
- agentData.channels.forEach((channel: { channel: string; key: string }) => {
159
- logger.info(`${channel.channel}: ${channel.key}`);
185
+ sectionHeader(`Canales (${agentData.channels.length})`);
186
+ agentData.channels.forEach((ch: any) => {
187
+ console.log(chalk.white(` ${chalk.hex('#22d3ee')(ch.channel || 'canal')}: ${ch.key || ''}`));
160
188
  });
161
189
  }
162
190
 
163
- logger.doubleDivider();
164
-
191
+ // AI Config
192
+ if (agentData.customAIConfig && agentData.aiProviders && agentData.aiProviders.length > 0) {
193
+ sectionHeader('Modelo IA');
194
+ agentData.aiProviders.forEach((ai: any) => {
195
+ label('Proveedor', ai.provider);
196
+ label('Modelo', ai.model);
197
+ });
198
+ }
199
+
200
+ console.log();
201
+ console.log(chalk.gray(' Usa --raw para ver el JSON completo'));
202
+ console.log();
203
+
165
204
  if (options.dev) {
166
205
  logger.warning('Ambiente: desarrollo');
167
206
  }
168
-
207
+
169
208
  } catch (error) {
170
209
  const message = error instanceof Error ? error.message : 'Error desconocido al obtener el agente';
171
210
  logger.error(message);
@@ -135,6 +135,7 @@ function printHeader(agentName: string) {
135
135
  const COMMANDS_HELP = `
136
136
  ${chalk.bold('Comandos disponibles:')}
137
137
  ${chalk.hex('#4CAF50')('/filter <tipo>')} Toggle filtro por tipo (ej: msg_in, error, tool_call)
138
+ ${chalk.hex('#4CAF50')('/filter clear')} Quitar todos los filtros
138
139
  ${chalk.hex('#4CAF50')('/filters')} Mostrar filtros activos
139
140
  ${chalk.hex('#4CAF50')('/clear')} Limpiar pantalla
140
141
  ${chalk.hex('#4CAF50')('/json')} Toggle modo JSON expandido
@@ -152,17 +153,28 @@ export const monitorCommand = new Command('monitor')
152
153
  .option('-f, --filter <types>', 'Filtrar por tipos separados por coma (ej: msg_in,msg_out)')
153
154
  .option('--no-session', 'Ocultar session ID')
154
155
  .option('--json', 'Modo JSON crudo (para piping)', false)
156
+ .option('-w, --workspace <id>', 'Workspace ID (sobreescribe config local)')
157
+ .option('-z, --zone <zone>', 'Zona LA o EU (sobreescribe config local)')
155
158
  .option('--dev', 'Usar ambiente de desarrollo', false)
156
159
  .action(async (options: {
157
160
  agentId: string;
158
161
  filter?: string;
159
162
  session: boolean;
160
163
  json: boolean;
164
+ workspace?: string;
165
+ zone?: string;
161
166
  dev: boolean;
162
167
  }) => {
163
168
  try {
164
169
  const credentials = await getStoredCredentials();
165
- const baseUrl = getBaseUrl(credentials.zone, options.dev);
170
+
171
+ const effectiveWorkspace = options.workspace || credentials.workspace;
172
+ const effectiveZone = (options.zone?.toUpperCase() === 'EU' ? 'EU' : options.zone?.toUpperCase() === 'LA' ? 'LA' : credentials.zone) as 'LA' | 'EU';
173
+ const baseUrl = getBaseUrl(effectiveZone, options.dev);
174
+
175
+ if (options.workspace || options.zone) {
176
+ console.log(chalk.hex('#FFA726')(`\n Modo soporte: workspace=${effectiveWorkspace} zona=${effectiveZone}`));
177
+ }
166
178
 
167
179
  // Filtros iniciales
168
180
  const activeFilters: Set<string> = new Set(
@@ -176,9 +188,9 @@ export const monitorCommand = new Command('monitor')
176
188
  try {
177
189
  process.stdout.write(chalk.gray(' Conectando con agente...'));
178
190
  const agent = new Agent({
179
- workspaceId: credentials.workspace,
191
+ workspaceId: effectiveWorkspace,
180
192
  apiKey: credentials.apiKey,
181
- zone: credentials.zone,
193
+ zone: effectiveZone,
182
194
  ...(options.dev && { customUrl: 'http://localhost:5090' }),
183
195
  });
184
196
  const info: any = await agent.getAgentById({ id: options.agentId });
@@ -291,7 +303,12 @@ export const monitorCommand = new Command('monitor')
291
303
  if (cmd.startsWith('/filter ')) {
292
304
  const filterType = cmd.substring(8).trim();
293
305
  if (!filterType) {
294
- console.log(chalk.gray(' Uso: /filter <tipo>'));
306
+ console.log(chalk.gray(' Uso: /filter <tipo> | /filter clear'));
307
+ return;
308
+ }
309
+ if (filterType === 'clear') {
310
+ activeFilters.clear();
311
+ console.log(chalk.gray(' Todos los filtros removidos (mostrando todos)'));
295
312
  return;
296
313
  }
297
314
  if (activeFilters.has(filterType)) {
@@ -1,5 +1,7 @@
1
1
  import inquirer from 'inquirer';
2
2
  import chalk from 'chalk';
3
+ import axios from 'axios';
4
+ import ora from 'ora';
3
5
  import { theme, section, kvPair } from '../../utils/ui';
4
6
 
5
7
  interface AgentConfig {
@@ -87,14 +89,195 @@ interface AgentExample {
87
89
  }
88
90
 
89
91
  const MODELS: Record<string, string[]> = {
90
- openai: ['gpt-4o', 'gpt-4', 'gpt-3.5-turbo', 'o1-preview', 'o1-mini'],
92
+ openai: ['gpt-4o', 'gpt-4o-mini', 'gpt-4', 'gpt-3.5-turbo', 'o1-preview', 'o1-mini'],
91
93
  claude: ['claude-3-5-sonnet-20241022', 'claude-3-opus-20240229', 'claude-3-haiku-20240307'],
92
94
  gemini: ['gemini-2.0-flash', 'gemini-1.5-pro', 'gemini-1.5-flash'],
93
95
  };
94
96
 
95
97
  const COLORS = ['blue', 'green', 'orange', 'gray', 'white'];
96
98
 
97
- export async function runAgentWizard(zone: string): Promise<AgentConfig> {
99
+ interface WizardContext {
100
+ zone: string;
101
+ workspaceId: string;
102
+ apiKey: string;
103
+ dev: boolean;
104
+ }
105
+
106
+ function getBaseUrl(zone: string, dev: boolean): string {
107
+ if (dev) return 'http://localhost:5090';
108
+ return zone === 'EU' ? 'https://apieu.plazbot.com' : 'https://api.plazbot.com';
109
+ }
110
+
111
+ const CHANNEL_CHOICES = [
112
+ { name: 'WhatsApp', value: 'whatsapp' },
113
+ { name: 'WhatsApp Business', value: 'whatsapp_business' },
114
+ { name: 'Instagram', value: 'instagram' },
115
+ { name: 'Messenger', value: 'facebook' },
116
+ ];
117
+
118
+ const CHANNEL_AGENT_MAP: Record<string, string> = {
119
+ whatsapp: 'whatsapp',
120
+ whatsapp_business: 'whatsapp',
121
+ instagram: 'instagram',
122
+ facebook: 'facebook',
123
+ };
124
+
125
+ async function connectChannelFlow(ctx: WizardContext): Promise<AgentChannel[]> {
126
+ const channels: AgentChannel[] = [];
127
+ const baseUrl = getBaseUrl(ctx.zone, ctx.dev);
128
+ const headers = {
129
+ 'Authorization': `Bearer ${ctx.apiKey}`,
130
+ 'x-workspace-id': ctx.workspaceId,
131
+ 'Content-Type': 'application/json',
132
+ };
133
+
134
+ let addMore = true;
135
+ while (addMore) {
136
+ const { channelType } = await (inquirer as any).prompt([{
137
+ type: 'list',
138
+ name: 'channelType',
139
+ message: 'Que canal deseas conectar?',
140
+ choices: CHANNEL_CHOICES,
141
+ }]);
142
+
143
+ const channelLabel = CHANNEL_CHOICES.find(c => c.value === channelType)?.name || channelType;
144
+
145
+ // Generar link de onboarding
146
+ const spinner = ora({ text: chalk.gray('Generando link de conexion...'), spinner: 'dots', color: 'green' }).start();
147
+
148
+ try {
149
+ const linkRes = await axios.post(
150
+ `${baseUrl}/api/workspace/${ctx.workspaceId}/onboarding-link`,
151
+ { type: channelType },
152
+ { headers }
153
+ );
154
+
155
+ if (!linkRes.data?.success) {
156
+ spinner.fail(chalk.hex('#EF5350')(`Error: ${linkRes.data?.message || 'No se pudo generar el link'}`));
157
+ const { tryManual } = await (inquirer as any).prompt([{
158
+ type: 'confirm', name: 'tryManual',
159
+ message: 'Deseas ingresar el numero manualmente?', default: false,
160
+ }]);
161
+ if (tryManual) {
162
+ const { key } = await (inquirer as any).prompt([
163
+ { type: 'input', name: 'key', message: `ID/numero del canal ${channelLabel}:`, validate: (v: string) => v.length > 0 || 'Requerido' },
164
+ ]);
165
+ channels.push({ channel: CHANNEL_AGENT_MAP[channelType], key, multianswer: false });
166
+ }
167
+ const { more } = await (inquirer as any).prompt([{ type: 'confirm', name: 'more', message: 'Conectar otro canal?', default: false }]);
168
+ addMore = more;
169
+ continue;
170
+ }
171
+
172
+ const shortUrl = linkRes.data.data?.shortUrl || '';
173
+ spinner.stop();
174
+
175
+ // Mostrar link
176
+ console.log();
177
+ console.log(chalk.hex('#4CAF50')(' ─'.repeat(32)));
178
+ console.log(chalk.bold(` Envia este link para conectar ${channelLabel}:`));
179
+ console.log();
180
+ console.log(' ' + chalk.hex('#22d3ee')(shortUrl));
181
+ console.log();
182
+ console.log(chalk.hex('#4CAF50')(' ─'.repeat(32)));
183
+ console.log();
184
+
185
+ // Snapshot de integraciones actuales
186
+ let prevIntegrationIds: Set<string> = new Set();
187
+ try {
188
+ const wkRes = await axios.get(`${baseUrl}/api/workspace/${ctx.workspaceId}`, { headers });
189
+ const integrations = wkRes.data?.integrations || [];
190
+ prevIntegrationIds = new Set(integrations.map((i: any) => i.id));
191
+ } catch {
192
+ // Si falla, se parte de vacio
193
+ }
194
+
195
+ // Polling para detectar nueva integracion
196
+ const pollSpinner = ora({ text: chalk.gray('Esperando conexion... (Ctrl+C para cancelar)'), spinner: 'dots', color: 'cyan' }).start();
197
+ const POLL_INTERVAL = 5000;
198
+ const MAX_POLLS = 60; // 5 minutos
199
+ let detected: any = null;
200
+
201
+ for (let i = 0; i < MAX_POLLS; i++) {
202
+ await new Promise(r => setTimeout(r, POLL_INTERVAL));
203
+ try {
204
+ const wkRes = await axios.get(`${baseUrl}/api/workspace/${ctx.workspaceId}`, { headers });
205
+ const integrations = wkRes.data?.integrations || [];
206
+
207
+ // Buscar integraciones nuevas (cualquier ID que no existia antes)
208
+ const newOnes = integrations.filter((ig: any) => !prevIntegrationIds.has(ig.id));
209
+
210
+ if (newOnes.length > 0) {
211
+ // Preferir la que coincida con el tipo solicitado
212
+ const targetType = channelType === 'whatsapp_business' ? 'whatsapp' : channelType;
213
+ const matchingNew = newOnes.find((ig: any) => ig.type === targetType);
214
+ detected = matchingNew || newOnes[0];
215
+ break;
216
+ }
217
+ } catch {
218
+ // Ignorar errores de poll individual
219
+ }
220
+ const elapsed = Math.floor(((i + 1) * POLL_INTERVAL) / 1000);
221
+ pollSpinner.text = chalk.gray(`Esperando conexion... ${elapsed}s`);
222
+ }
223
+
224
+ if (detected) {
225
+ const detectedName = detected.aliasName || detected.cellphoneNumberFormat || detected.cellphoneNumber || detected.srcName || detected.id;
226
+ pollSpinner.succeed(chalk.hex('#4ade80')(`Conexion detectada: ${detectedName} (${channelLabel})`));
227
+
228
+ const { associate } = await (inquirer as any).prompt([{
229
+ type: 'confirm', name: 'associate',
230
+ message: 'Deseas asociar este canal al agente?', default: true,
231
+ }]);
232
+
233
+ if (associate) {
234
+ channels.push({
235
+ channel: CHANNEL_AGENT_MAP[channelType],
236
+ key: detected.id,
237
+ multianswer: false,
238
+ });
239
+ console.log(chalk.hex('#4ade80')(' Canal asociado correctamente.'));
240
+ }
241
+ } else {
242
+ pollSpinner.warn(chalk.hex('#FFA726')('Timeout: no se detecto una conexion.'));
243
+ const { action } = await (inquirer as any).prompt([{
244
+ type: 'list', name: 'action',
245
+ message: 'Que deseas hacer?',
246
+ choices: [
247
+ { name: 'Ingresar numero/ID manualmente', value: 'manual' },
248
+ { name: 'Saltar este paso', value: 'skip' },
249
+ ],
250
+ }]);
251
+ if (action === 'manual') {
252
+ const { key } = await (inquirer as any).prompt([
253
+ { type: 'input', name: 'key', message: `ID/numero del canal ${channelLabel}:`, validate: (v: string) => v.length > 0 || 'Requerido' },
254
+ ]);
255
+ channels.push({ channel: CHANNEL_AGENT_MAP[channelType], key, multianswer: false });
256
+ }
257
+ }
258
+
259
+ } catch (err) {
260
+ spinner.fail(chalk.hex('#EF5350')(`Error al generar link: ${err instanceof Error ? err.message : err}`));
261
+ const { tryManual } = await (inquirer as any).prompt([{
262
+ type: 'confirm', name: 'tryManual',
263
+ message: 'Deseas ingresar el numero manualmente?', default: false,
264
+ }]);
265
+ if (tryManual) {
266
+ const { key } = await (inquirer as any).prompt([
267
+ { type: 'input', name: 'key', message: `ID/numero del canal ${channelLabel}:`, validate: (v: string) => v.length > 0 || 'Requerido' },
268
+ ]);
269
+ channels.push({ channel: CHANNEL_AGENT_MAP[channelType], key, multianswer: false });
270
+ }
271
+ }
272
+
273
+ const { more } = await (inquirer as any).prompt([{ type: 'confirm', name: 'more', message: 'Conectar otro canal?', default: false }]);
274
+ addMore = more;
275
+ }
276
+
277
+ return channels;
278
+ }
279
+
280
+ export async function runAgentWizard(zone: string, workspaceId?: string, apiKey?: string, dev?: boolean): Promise<AgentConfig> {
98
281
  console.log(section('Crear nuevo agente de IA'));
99
282
  console.log(theme.muted(' Responde las siguientes preguntas para configurar tu agente.\n'));
100
283
 
@@ -369,31 +552,32 @@ export async function runAgentWizard(zone: string): Promise<AgentConfig> {
369
552
  const aiConfig = await (inquirer as any).prompt([
370
553
  { type: 'list', name: 'model', message: 'Modelo:', choices: models },
371
554
  { type: 'password', name: 'apiToken', message: 'API Token:', mask: '*', validate: (v: string) => v.length > 0 || 'Requerido' },
372
- { type: 'number', name: 'temperature', message: 'Temperatura (0-2):', default: 0.7 },
373
- { type: 'number', name: 'maxTokens', message: 'Max tokens (1024-16384):', default: 4096 },
374
555
  ]);
375
556
 
376
557
  aiProviders.push({
377
558
  provider: ai.provider,
378
559
  model: aiConfig.model,
379
560
  apiToken: aiConfig.apiToken,
380
- temperature: aiConfig.temperature,
381
- maxTokens: aiConfig.maxTokens,
561
+ temperature: 0.7,
562
+ maxTokens: 4096,
382
563
  isDefault: true,
383
564
  });
384
565
  }
385
566
 
386
- // Paso 7: Canal WhatsApp
387
- console.log(theme.bold('\n Paso 7/8: Canal WhatsApp'));
388
- const channels: AgentChannel[] = [];
389
- const { addWhatsApp } = await (inquirer as any).prompt([{
567
+ // Paso 7: Conectar canal
568
+ console.log(theme.bold('\n Paso 7/8: Conectar canal'));
569
+ let channels: AgentChannel[] = [];
570
+ const { addChannel } = await (inquirer as any).prompt([{
390
571
  type: 'confirm',
391
- name: 'addWhatsApp',
392
- message: 'Conectar a un numero de WhatsApp?',
572
+ name: 'addChannel',
573
+ message: 'Deseas conectar un canal (WhatsApp, Instagram, Messenger)?',
393
574
  default: false,
394
575
  }]);
395
576
 
396
- if (addWhatsApp) {
577
+ if (addChannel && workspaceId && apiKey) {
578
+ channels = await connectChannelFlow({ zone, workspaceId, apiKey, dev: dev || false });
579
+ } else if (addChannel) {
580
+ // Fallback manual si no hay credenciales para API
397
581
  const wa = await (inquirer as any).prompt([
398
582
  { type: 'input', name: 'key', message: 'Numero de WhatsApp (con codigo de pais):', validate: (v: string) => v.length > 0 || 'Requerido' },
399
583
  { type: 'confirm', name: 'multianswer', message: 'Permitir multiples respuestas?', default: false },
@@ -468,7 +652,13 @@ export async function runAgentWizard(zone: string): Promise<AgentConfig> {
468
652
  console.log(kvPair('Servicios', String(config.services.length)));
469
653
  console.log(kvPair('Acciones', String(config.actions.length)));
470
654
  console.log(kvPair('AI Provider', config.customAIConfig ? config.aiProviders[0]?.provider + ' / ' + config.aiProviders[0]?.model : 'Default (Plazbot)'));
471
- console.log(kvPair('Canal WhatsApp', config.channels.length > 0 ? config.channels[0].key : 'No configurado'));
655
+ if (config.channels.length > 0) {
656
+ config.channels.forEach((ch, i) => {
657
+ console.log(kvPair(`Canal ${i + 1} (${ch.channel})`, ch.key));
658
+ });
659
+ } else {
660
+ console.log(kvPair('Canales', 'No configurado'));
661
+ }
472
662
  console.log();
473
663
 
474
664
  return config;