plazbot-cli 0.2.18 → 0.2.20

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.
@@ -26,22 +26,145 @@ const VALID_AGENT_FIELDS = new Set([
26
26
  'fUseAutomationFlowWidget', 'iconWidget',
27
27
  'enableAvatarWidget', 'avatarGender', 'avatarVoiceId', 'avatarLanguage',
28
28
  ]);
29
+ // Campos que el schema espera como string (no pueden ser null)
30
+ const STRING_FIELDS = new Set([
31
+ 'name', 'description', 'prompt', 'zone', 'timezone', 'color',
32
+ 'question', 'responseMode', 'iconWidget', 'avatarGender',
33
+ 'avatarVoiceId', 'avatarLanguage', 'urlWhatsappWidget', 'nameWidget',
34
+ ]);
35
+ // Campos que el schema espera como array (no pueden ser null)
36
+ const ARRAY_FIELDS = new Set([
37
+ 'tags', 'examples', 'channels', 'services', 'actions', 'aiProviders',
38
+ ]);
39
+ function cleanRequiredFields(fields) {
40
+ if (!fields || !Array.isArray(fields))
41
+ return [];
42
+ return fields.map((f) => {
43
+ const clean = {};
44
+ if (f.name)
45
+ clean.name = f.name;
46
+ if (f.description)
47
+ clean.description = f.description;
48
+ if (f.promptHint)
49
+ clean.promptHint = f.promptHint;
50
+ if (f.type)
51
+ clean.type = f.type;
52
+ // properties debe ser array o no existir
53
+ if (f.properties && Array.isArray(f.properties)) {
54
+ clean.properties = f.properties.map((p) => ({
55
+ name: p.name || '',
56
+ type: p.type || 'string',
57
+ }));
58
+ }
59
+ // Si properties es null o no es array, no incluirlo
60
+ return clean;
61
+ });
62
+ }
63
+ function cleanServices(services) {
64
+ if (!services || !Array.isArray(services))
65
+ return [];
66
+ return services.map((svc) => {
67
+ const clean = {
68
+ intent: svc.intent || '',
69
+ reference: svc.reference || '',
70
+ enabled: svc.enabled ?? true,
71
+ method: svc.method || 'GET',
72
+ endpoint: svc.endpoint || '',
73
+ requiredFields: cleanRequiredFields(svc.requiredFields),
74
+ };
75
+ if (svc.tags && Array.isArray(svc.tags))
76
+ clean.tags = svc.tags;
77
+ if (svc.headers && typeof svc.headers === 'object')
78
+ clean.headers = svc.headers;
79
+ if (svc.bodyTemplate && typeof svc.bodyTemplate === 'object')
80
+ clean.bodyTemplate = svc.bodyTemplate;
81
+ if (svc.bodySchema && typeof svc.bodySchema === 'object')
82
+ clean.bodySchema = svc.bodySchema;
83
+ if (svc.responseMapping && typeof svc.responseMapping === 'object')
84
+ clean.responseMapping = svc.responseMapping;
85
+ if (typeof svc.responseMessage === 'string')
86
+ clean.responseMessage = svc.responseMessage;
87
+ if (svc.responseConditions && Array.isArray(svc.responseConditions)) {
88
+ clean.responseConditions = svc.responseConditions.map((rc) => {
89
+ const c = { condition: rc.condition || '' };
90
+ if (typeof rc.message === 'string')
91
+ c.message = rc.message;
92
+ if (typeof rc.nextService === 'string')
93
+ c.nextService = rc.nextService;
94
+ return c;
95
+ });
96
+ }
97
+ if (typeof svc.action === 'string')
98
+ clean.action = svc.action;
99
+ return clean;
100
+ });
101
+ }
102
+ function cleanActions(actions) {
103
+ if (!actions || !Array.isArray(actions))
104
+ return [];
105
+ return actions.map((act) => {
106
+ const clean = {
107
+ intent: act.intent || '',
108
+ reference: act.reference || '',
109
+ enabled: act.enabled ?? true,
110
+ action: Array.isArray(act.action) ? act.action.map((a) => ({
111
+ type: a.type || '',
112
+ value: a.value ?? '',
113
+ })) : [],
114
+ };
115
+ if (act.tags && Array.isArray(act.tags))
116
+ clean.tags = act.tags;
117
+ clean.requiredFields = cleanRequiredFields(act.requiredFields);
118
+ if (typeof act.responseMessage === 'string')
119
+ clean.responseMessage = act.responseMessage;
120
+ if (typeof act.responseJson === 'boolean')
121
+ clean.responseJson = act.responseJson;
122
+ return clean;
123
+ });
124
+ }
29
125
  function cleanAgentForExport(agentData) {
30
126
  const cleaned = {};
31
127
  for (const key of Object.keys(agentData)) {
32
- if (VALID_AGENT_FIELDS.has(key)) {
33
- cleaned[key] = agentData[key];
128
+ if (!VALID_AGENT_FIELDS.has(key))
129
+ continue;
130
+ const value = agentData[key];
131
+ // Eliminar campos null en campos string
132
+ if (STRING_FIELDS.has(key)) {
133
+ if (value !== null && value !== undefined) {
134
+ cleaned[key] = String(value);
135
+ }
136
+ continue;
137
+ }
138
+ // Asegurar que campos array sean arrays
139
+ if (ARRAY_FIELDS.has(key)) {
140
+ if (key === 'services') {
141
+ cleaned[key] = cleanServices(value);
142
+ }
143
+ else if (key === 'actions') {
144
+ cleaned[key] = cleanActions(value);
145
+ }
146
+ else if (Array.isArray(value)) {
147
+ cleaned[key] = value;
148
+ }
149
+ else {
150
+ cleaned[key] = [];
151
+ }
152
+ continue;
153
+ }
154
+ // Eliminar nulls genericos
155
+ if (value !== null && value !== undefined) {
156
+ cleaned[key] = value;
34
157
  }
35
158
  }
36
- // Limpiar channels (quitar keys especificas del workspace origen)
159
+ // Limpiar channels
37
160
  if (cleaned.channels && Array.isArray(cleaned.channels)) {
38
161
  cleaned.channels = cleaned.channels.map((ch) => ({
39
- channel: ch.channel,
40
- key: ch.key,
162
+ channel: ch.channel || '',
163
+ key: ch.key || '',
41
164
  multianswer: ch.multianswer || false,
42
165
  }));
43
166
  }
44
- // Limpiar aiProviders (quitar tokens sensibles opcionalmente)
167
+ // Limpiar aiProviders
45
168
  if (cleaned.aiProviders && Array.isArray(cleaned.aiProviders)) {
46
169
  cleaned.aiProviders = cleaned.aiProviders.map((ai) => ({
47
170
  provider: ai.provider,
@@ -52,6 +175,13 @@ function cleanAgentForExport(agentData) {
52
175
  isDefault: ai.isDefault,
53
176
  }));
54
177
  }
178
+ // Limpiar examples
179
+ if (cleaned.examples && Array.isArray(cleaned.examples)) {
180
+ cleaned.examples = cleaned.examples.map((ex) => ({
181
+ value: ex.value || '',
182
+ color: ex.color || 'blue',
183
+ }));
184
+ }
55
185
  return cleaned;
56
186
  }
57
187
  exports.exportCommand = new commander_1.Command('export')
@@ -10,6 +10,7 @@ const plazbot_1 = require("plazbot");
10
10
  const credentials_1 = require("../../utils/credentials");
11
11
  const logger_1 = require("../../utils/logger");
12
12
  const ui_1 = require("../../utils/ui");
13
+ const wizard_1 = require("./wizard");
13
14
  // Plantilla: Clinica
14
15
  const plantillaClinica = {
15
16
  "name": "Asistente Clinica Salud",
@@ -704,23 +705,24 @@ exports.templatesCommand = new commander_1.Command('templates')
704
705
  }]);
705
706
  placeholderAnswers[ph.key] = value.trim();
706
707
  }
707
- // Paso 3: WhatsApp
708
- console.log(ui_1.theme.bold('\n Canal WhatsApp'));
709
- const { deployWhatsApp } = await inquirer_1.default.prompt([{
708
+ // Paso 3: Canales (WhatsApp, Messenger, Instagram, Telegram)
709
+ console.log(ui_1.theme.bold('\n Conexion de canales'));
710
+ const { wantChannels } = await inquirer_1.default.prompt([{
710
711
  type: 'confirm',
711
- name: 'deployWhatsApp',
712
- message: 'Deseas desplegarlo en WhatsApp?',
712
+ name: 'wantChannels',
713
+ message: 'Deseas conectar canales (WhatsApp, Messenger, Instagram, Telegram)?',
713
714
  default: true,
714
715
  }]);
715
- let whatsappNumber = '';
716
- if (deployWhatsApp) {
717
- const { number } = await inquirer_1.default.prompt([{
718
- type: 'input',
719
- name: 'number',
720
- message: 'Numero de WhatsApp (con codigo de pais, ej: +51999888777):',
721
- validate: (v) => v.trim().length > 0 || 'El numero es requerido',
722
- }]);
723
- whatsappNumber = number.trim();
716
+ const credentials = await (0, credentials_1.getStoredCredentials)();
717
+ let connectedChannels = [];
718
+ if (wantChannels) {
719
+ const wizardCtx = {
720
+ zone: credentials.zone,
721
+ workspaceId: credentials.workspace,
722
+ apiKey: credentials.apiKey,
723
+ dev: !!options.dev,
724
+ };
725
+ connectedChannels = await (0, wizard_1.connectChannelFlow)(wizardCtx);
724
726
  }
725
727
  // Paso 4: API Key de OpenAI
726
728
  console.log(ui_1.theme.bold('\n Configuracion de IA'));
@@ -752,8 +754,8 @@ exports.templatesCommand = new commander_1.Command('templates')
752
754
  const configClone = JSON.parse(JSON.stringify(template.config));
753
755
  const finalConfig = replacePlaceholders(configClone, placeholderAnswers);
754
756
  finalConfig.timezone = timezone;
755
- if (deployWhatsApp && whatsappNumber) {
756
- finalConfig.channels = [{ channel: 'whatsapp', key: whatsappNumber }];
757
+ if (connectedChannels.length > 0) {
758
+ finalConfig.channels = connectedChannels;
757
759
  }
758
760
  else {
759
761
  finalConfig.channels = [];
@@ -773,7 +775,7 @@ exports.templatesCommand = new commander_1.Command('templates')
773
775
  for (const ph of template.placeholders) {
774
776
  console.log((0, ui_1.kvPair)(ph.label, placeholderAnswers[ph.key]));
775
777
  }
776
- console.log((0, ui_1.kvPair)('WhatsApp', deployWhatsApp ? whatsappNumber : 'No configurado'));
778
+ console.log((0, ui_1.kvPair)('Canales', connectedChannels.length > 0 ? connectedChannels.map(c => `${c.channel} (${c.key})`).join(', ') : 'No configurado'));
777
779
  console.log((0, ui_1.kvPair)('API Key OpenAI', apiToken ? 'Configurada' : 'No configurada (el agente no funcionara sin ella)'));
778
780
  console.log((0, ui_1.kvPair)('Zona horaria', timezone));
779
781
  console.log((0, ui_1.kvPair)('Acciones', String(finalConfig.actions.length)));
@@ -790,7 +792,6 @@ exports.templatesCommand = new commander_1.Command('templates')
790
792
  return;
791
793
  }
792
794
  // Paso 7: Crear agente
793
- const credentials = await (0, credentials_1.getStoredCredentials)();
794
795
  const agent = new plazbot_1.Agent({
795
796
  workspaceId: credentials.workspace,
796
797
  apiKey: credentials.apiKey,
@@ -808,16 +809,20 @@ exports.templatesCommand = new commander_1.Command('templates')
808
809
  }
809
810
  logger_1.logger.label('Nombre', finalConfig.name);
810
811
  logger_1.logger.label('Industria', template.industry);
811
- if (deployWhatsApp) {
812
- logger_1.logger.label('WhatsApp', whatsappNumber);
812
+ if (connectedChannels.length > 0) {
813
+ logger_1.logger.label('Canales', connectedChannels.map(c => `${c.channel} (${c.key})`).join(', '));
813
814
  }
814
815
  logger_1.logger.label('Acciones', String(finalConfig.actions.length));
815
816
  console.log();
816
817
  console.log((0, ui_1.section)('Siguientes pasos'));
817
- console.log(ui_1.theme.bold(' 1.') + ' Crea tu cuenta en ' + ui_1.theme.success('appla.plazbot.com/register'));
818
- console.log(ui_1.theme.bold(' 2.') + ' Ve a Agentes en el menu e importa el archivo');
819
- console.log(ui_1.theme.bold(' 3.') + ' Conecta WhatsApp en Ajustes > Integraciones');
820
- console.log(ui_1.theme.bold(' 4.') + ' Agrega tu API Key de OpenAI en Modelos de IA');
818
+ if (connectedChannels.length === 0) {
819
+ console.log(ui_1.theme.bold(' 1.') + ' Conecta canales desde: ' + ui_1.theme.success('plazbot agent chat -a <agentId>'));
820
+ console.log(ui_1.theme.bold(' 2.') + ' O conecta desde la plataforma en Ajustes > Integraciones');
821
+ }
822
+ else {
823
+ console.log(ui_1.theme.bold(' 1.') + ' Tu agente ya tiene canales conectados y activados');
824
+ }
825
+ console.log(ui_1.theme.bold(` ${connectedChannels.length === 0 ? '3' : '2'}.`) + ' Agrega tu API Key de OpenAI si no la configuraste');
821
826
  console.log();
822
827
  if (!apiToken) {
823
828
  logger_1.logger.warning('Recuerda configurar tu API Key de OpenAI para que el agente funcione:');
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.connectChannelFlow = connectChannelFlow;
6
7
  exports.runAgentWizard = runAgentWizard;
7
8
  const inquirer_1 = __importDefault(require("inquirer"));
8
9
  const chalk_1 = __importDefault(require("chalk"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plazbot-cli",
3
- "version": "0.2.18",
3
+ "version": "0.2.20",
4
4
  "description": "CLI para Plazbot SDK",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -22,25 +22,134 @@ const VALID_AGENT_FIELDS = new Set([
22
22
  'enableAvatarWidget', 'avatarGender', 'avatarVoiceId', 'avatarLanguage',
23
23
  ]);
24
24
 
25
+ // Campos que el schema espera como string (no pueden ser null)
26
+ const STRING_FIELDS = new Set([
27
+ 'name', 'description', 'prompt', 'zone', 'timezone', 'color',
28
+ 'question', 'responseMode', 'iconWidget', 'avatarGender',
29
+ 'avatarVoiceId', 'avatarLanguage', 'urlWhatsappWidget', 'nameWidget',
30
+ ]);
31
+
32
+ // Campos que el schema espera como array (no pueden ser null)
33
+ const ARRAY_FIELDS = new Set([
34
+ 'tags', 'examples', 'channels', 'services', 'actions', 'aiProviders',
35
+ ]);
36
+
37
+ function cleanRequiredFields(fields: any): any[] {
38
+ if (!fields || !Array.isArray(fields)) return [];
39
+ return fields.map((f: any) => {
40
+ const clean: any = {};
41
+ if (f.name) clean.name = f.name;
42
+ if (f.description) clean.description = f.description;
43
+ if (f.promptHint) clean.promptHint = f.promptHint;
44
+ if (f.type) clean.type = f.type;
45
+ // properties debe ser array o no existir
46
+ if (f.properties && Array.isArray(f.properties)) {
47
+ clean.properties = f.properties.map((p: any) => ({
48
+ name: p.name || '',
49
+ type: p.type || 'string',
50
+ }));
51
+ }
52
+ // Si properties es null o no es array, no incluirlo
53
+ return clean;
54
+ });
55
+ }
56
+
57
+ function cleanServices(services: any): any[] {
58
+ if (!services || !Array.isArray(services)) return [];
59
+ return services.map((svc: any) => {
60
+ const clean: any = {
61
+ intent: svc.intent || '',
62
+ reference: svc.reference || '',
63
+ enabled: svc.enabled ?? true,
64
+ method: svc.method || 'GET',
65
+ endpoint: svc.endpoint || '',
66
+ requiredFields: cleanRequiredFields(svc.requiredFields),
67
+ };
68
+ if (svc.tags && Array.isArray(svc.tags)) clean.tags = svc.tags;
69
+ if (svc.headers && typeof svc.headers === 'object') clean.headers = svc.headers;
70
+ if (svc.bodyTemplate && typeof svc.bodyTemplate === 'object') clean.bodyTemplate = svc.bodyTemplate;
71
+ if (svc.bodySchema && typeof svc.bodySchema === 'object') clean.bodySchema = svc.bodySchema;
72
+ if (svc.responseMapping && typeof svc.responseMapping === 'object') clean.responseMapping = svc.responseMapping;
73
+ if (typeof svc.responseMessage === 'string') clean.responseMessage = svc.responseMessage;
74
+ if (svc.responseConditions && Array.isArray(svc.responseConditions)) {
75
+ clean.responseConditions = svc.responseConditions.map((rc: any) => {
76
+ const c: any = { condition: rc.condition || '' };
77
+ if (typeof rc.message === 'string') c.message = rc.message;
78
+ if (typeof rc.nextService === 'string') c.nextService = rc.nextService;
79
+ return c;
80
+ });
81
+ }
82
+ if (typeof svc.action === 'string') clean.action = svc.action;
83
+ return clean;
84
+ });
85
+ }
86
+
87
+ function cleanActions(actions: any): any[] {
88
+ if (!actions || !Array.isArray(actions)) return [];
89
+ return actions.map((act: any) => {
90
+ const clean: any = {
91
+ intent: act.intent || '',
92
+ reference: act.reference || '',
93
+ enabled: act.enabled ?? true,
94
+ action: Array.isArray(act.action) ? act.action.map((a: any) => ({
95
+ type: a.type || '',
96
+ value: a.value ?? '',
97
+ })) : [],
98
+ };
99
+ if (act.tags && Array.isArray(act.tags)) clean.tags = act.tags;
100
+ clean.requiredFields = cleanRequiredFields(act.requiredFields);
101
+ if (typeof act.responseMessage === 'string') clean.responseMessage = act.responseMessage;
102
+ if (typeof act.responseJson === 'boolean') clean.responseJson = act.responseJson;
103
+ return clean;
104
+ });
105
+ }
106
+
25
107
  function cleanAgentForExport(agentData: any): any {
26
108
  const cleaned: any = {};
27
109
 
28
110
  for (const key of Object.keys(agentData)) {
29
- if (VALID_AGENT_FIELDS.has(key)) {
30
- cleaned[key] = agentData[key];
111
+ if (!VALID_AGENT_FIELDS.has(key)) continue;
112
+
113
+ const value = agentData[key];
114
+
115
+ // Eliminar campos null en campos string
116
+ if (STRING_FIELDS.has(key)) {
117
+ if (value !== null && value !== undefined) {
118
+ cleaned[key] = String(value);
119
+ }
120
+ continue;
121
+ }
122
+
123
+ // Asegurar que campos array sean arrays
124
+ if (ARRAY_FIELDS.has(key)) {
125
+ if (key === 'services') {
126
+ cleaned[key] = cleanServices(value);
127
+ } else if (key === 'actions') {
128
+ cleaned[key] = cleanActions(value);
129
+ } else if (Array.isArray(value)) {
130
+ cleaned[key] = value;
131
+ } else {
132
+ cleaned[key] = [];
133
+ }
134
+ continue;
135
+ }
136
+
137
+ // Eliminar nulls genericos
138
+ if (value !== null && value !== undefined) {
139
+ cleaned[key] = value;
31
140
  }
32
141
  }
33
142
 
34
- // Limpiar channels (quitar keys especificas del workspace origen)
143
+ // Limpiar channels
35
144
  if (cleaned.channels && Array.isArray(cleaned.channels)) {
36
145
  cleaned.channels = cleaned.channels.map((ch: any) => ({
37
- channel: ch.channel,
38
- key: ch.key,
146
+ channel: ch.channel || '',
147
+ key: ch.key || '',
39
148
  multianswer: ch.multianswer || false,
40
149
  }));
41
150
  }
42
151
 
43
- // Limpiar aiProviders (quitar tokens sensibles opcionalmente)
152
+ // Limpiar aiProviders
44
153
  if (cleaned.aiProviders && Array.isArray(cleaned.aiProviders)) {
45
154
  cleaned.aiProviders = cleaned.aiProviders.map((ai: any) => ({
46
155
  provider: ai.provider,
@@ -52,6 +161,14 @@ function cleanAgentForExport(agentData: any): any {
52
161
  }));
53
162
  }
54
163
 
164
+ // Limpiar examples
165
+ if (cleaned.examples && Array.isArray(cleaned.examples)) {
166
+ cleaned.examples = cleaned.examples.map((ex: any) => ({
167
+ value: ex.value || '',
168
+ color: ex.color || 'blue',
169
+ }));
170
+ }
171
+
55
172
  return cleaned;
56
173
  }
57
174
 
@@ -5,6 +5,7 @@ import { getStoredCredentials } from '../../utils/credentials';
5
5
  import { logger } from '../../utils/logger';
6
6
  import { theme, section, kvPair, createTable, createSpinner } from '../../utils/ui';
7
7
  import { AgentCommandOptions } from '../../types/agent';
8
+ import { connectChannelFlow, WizardContext } from './wizard';
8
9
 
9
10
  interface TemplatePlaceholder {
10
11
  key: string;
@@ -727,24 +728,25 @@ export const templatesCommand = new Command('templates')
727
728
  placeholderAnswers[ph.key] = value.trim();
728
729
  }
729
730
 
730
- // Paso 3: WhatsApp
731
- console.log(theme.bold('\n Canal WhatsApp'));
732
- const { deployWhatsApp } = await (inquirer as any).prompt([{
731
+ // Paso 3: Canales (WhatsApp, Messenger, Instagram, Telegram)
732
+ console.log(theme.bold('\n Conexion de canales'));
733
+ const { wantChannels } = await (inquirer as any).prompt([{
733
734
  type: 'confirm',
734
- name: 'deployWhatsApp',
735
- message: 'Deseas desplegarlo en WhatsApp?',
735
+ name: 'wantChannels',
736
+ message: 'Deseas conectar canales (WhatsApp, Messenger, Instagram, Telegram)?',
736
737
  default: true,
737
738
  }]);
738
739
 
739
- let whatsappNumber = '';
740
- if (deployWhatsApp) {
741
- const { number } = await (inquirer as any).prompt([{
742
- type: 'input',
743
- name: 'number',
744
- message: 'Numero de WhatsApp (con codigo de pais, ej: +51999888777):',
745
- validate: (v: string) => v.trim().length > 0 || 'El numero es requerido',
746
- }]);
747
- whatsappNumber = number.trim();
740
+ const credentials = await getStoredCredentials();
741
+ let connectedChannels: { channel: string; key: string; multianswer: boolean }[] = [];
742
+ if (wantChannels) {
743
+ const wizardCtx: WizardContext = {
744
+ zone: credentials.zone,
745
+ workspaceId: credentials.workspace,
746
+ apiKey: credentials.apiKey,
747
+ dev: !!options.dev,
748
+ };
749
+ connectedChannels = await connectChannelFlow(wizardCtx);
748
750
  }
749
751
 
750
752
  // Paso 4: API Key de OpenAI
@@ -782,8 +784,8 @@ export const templatesCommand = new Command('templates')
782
784
 
783
785
  finalConfig.timezone = timezone;
784
786
 
785
- if (deployWhatsApp && whatsappNumber) {
786
- finalConfig.channels = [{ channel: 'whatsapp', key: whatsappNumber }];
787
+ if (connectedChannels.length > 0) {
788
+ finalConfig.channels = connectedChannels;
787
789
  } else {
788
790
  finalConfig.channels = [];
789
791
  }
@@ -805,7 +807,7 @@ export const templatesCommand = new Command('templates')
805
807
  for (const ph of template.placeholders) {
806
808
  console.log(kvPair(ph.label, placeholderAnswers[ph.key]));
807
809
  }
808
- console.log(kvPair('WhatsApp', deployWhatsApp ? whatsappNumber : 'No configurado'));
810
+ console.log(kvPair('Canales', connectedChannels.length > 0 ? connectedChannels.map(c => `${c.channel} (${c.key})`).join(', ') : 'No configurado'));
809
811
  console.log(kvPair('API Key OpenAI', apiToken ? 'Configurada' : 'No configurada (el agente no funcionara sin ella)'));
810
812
  console.log(kvPair('Zona horaria', timezone));
811
813
  console.log(kvPair('Acciones', String(finalConfig.actions.length)));
@@ -825,8 +827,6 @@ export const templatesCommand = new Command('templates')
825
827
  }
826
828
 
827
829
  // Paso 7: Crear agente
828
- const credentials = await getStoredCredentials();
829
-
830
830
  const agent = new Agent({
831
831
  workspaceId: credentials.workspace,
832
832
  apiKey: credentials.apiKey,
@@ -849,17 +849,20 @@ export const templatesCommand = new Command('templates')
849
849
  }
850
850
  logger.label('Nombre', finalConfig.name);
851
851
  logger.label('Industria', template.industry);
852
- if (deployWhatsApp) {
853
- logger.label('WhatsApp', whatsappNumber);
852
+ if (connectedChannels.length > 0) {
853
+ logger.label('Canales', connectedChannels.map(c => `${c.channel} (${c.key})`).join(', '));
854
854
  }
855
855
  logger.label('Acciones', String(finalConfig.actions.length));
856
856
 
857
857
  console.log();
858
858
  console.log(section('Siguientes pasos'));
859
- console.log(theme.bold(' 1.') + ' Crea tu cuenta en ' + theme.success('appla.plazbot.com/register'));
860
- console.log(theme.bold(' 2.') + ' Ve a Agentes en el menu e importa el archivo');
861
- console.log(theme.bold(' 3.') + ' Conecta WhatsApp en Ajustes > Integraciones');
862
- console.log(theme.bold(' 4.') + ' Agrega tu API Key de OpenAI en Modelos de IA');
859
+ if (connectedChannels.length === 0) {
860
+ console.log(theme.bold(' 1.') + ' Conecta canales desde: ' + theme.success('plazbot agent chat -a <agentId>'));
861
+ console.log(theme.bold(' 2.') + ' O conecta desde la plataforma en Ajustes > Integraciones');
862
+ } else {
863
+ console.log(theme.bold(' 1.') + ' Tu agente ya tiene canales conectados y activados');
864
+ }
865
+ console.log(theme.bold(` ${connectedChannels.length === 0 ? '3' : '2'}.`) + ' Agrega tu API Key de OpenAI si no la configuraste');
863
866
  console.log();
864
867
 
865
868
  if (!apiToken) {
@@ -96,7 +96,7 @@ const MODELS: Record<string, string[]> = {
96
96
 
97
97
  const COLORS = ['blue', 'green', 'orange', 'gray', 'white'];
98
98
 
99
- interface WizardContext {
99
+ export interface WizardContext {
100
100
  zone: string;
101
101
  workspaceId: string;
102
102
  apiKey: string;
@@ -160,7 +160,7 @@ async function activateIntegration(baseUrl: string, workspaceId: string, integra
160
160
  }
161
161
  }
162
162
 
163
- async function connectChannelFlow(ctx: WizardContext): Promise<AgentChannel[]> {
163
+ export async function connectChannelFlow(ctx: WizardContext): Promise<AgentChannel[]> {
164
164
  const channels: AgentChannel[] = [];
165
165
  const baseUrl = getBaseUrl(ctx.zone, ctx.dev);
166
166
  const headers = {