plazbot-cli 0.2.17 → 0.2.19
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.
|
@@ -0,0 +1,352 @@
|
|
|
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.crossCopyCommand = exports.exportCommand = 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 ui_1 = require("../../utils/ui");
|
|
12
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
16
|
+
// Campos validos segun el schema de agentValidation.ts (additionalProperties: false)
|
|
17
|
+
const VALID_AGENT_FIELDS = new Set([
|
|
18
|
+
'name', 'description', 'prompt', 'zone', 'timezone', 'buffer',
|
|
19
|
+
'color', 'question', 'enable', 'tags', 'showInChat',
|
|
20
|
+
'instructions', 'person', 'fallbacks', 'rules',
|
|
21
|
+
'examples', 'channels', 'services', 'actions',
|
|
22
|
+
'useToolCalling', 'customAIConfig', 'aiProviders',
|
|
23
|
+
'responseMode', 'batchWaitSeconds',
|
|
24
|
+
'enableWidget', 'enableWhatsappWidget', 'urlWhatsappWidget',
|
|
25
|
+
'formWidget', 'darkWidget', 'nameWidget', 'initialShowWidget',
|
|
26
|
+
'fUseAutomationFlowWidget', 'iconWidget',
|
|
27
|
+
'enableAvatarWidget', 'avatarGender', 'avatarVoiceId', 'avatarLanguage',
|
|
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
|
+
}
|
|
125
|
+
function cleanAgentForExport(agentData) {
|
|
126
|
+
const cleaned = {};
|
|
127
|
+
for (const key of Object.keys(agentData)) {
|
|
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;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Limpiar channels
|
|
160
|
+
if (cleaned.channels && Array.isArray(cleaned.channels)) {
|
|
161
|
+
cleaned.channels = cleaned.channels.map((ch) => ({
|
|
162
|
+
channel: ch.channel || '',
|
|
163
|
+
key: ch.key || '',
|
|
164
|
+
multianswer: ch.multianswer || false,
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
// Limpiar aiProviders
|
|
168
|
+
if (cleaned.aiProviders && Array.isArray(cleaned.aiProviders)) {
|
|
169
|
+
cleaned.aiProviders = cleaned.aiProviders.map((ai) => ({
|
|
170
|
+
provider: ai.provider,
|
|
171
|
+
model: ai.model,
|
|
172
|
+
apiToken: ai.apiToken || '',
|
|
173
|
+
temperature: ai.temperature,
|
|
174
|
+
maxTokens: ai.maxTokens,
|
|
175
|
+
isDefault: ai.isDefault,
|
|
176
|
+
}));
|
|
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
|
+
}
|
|
185
|
+
return cleaned;
|
|
186
|
+
}
|
|
187
|
+
exports.exportCommand = new commander_1.Command('export')
|
|
188
|
+
.description('Exportar configuracion limpia de un agente (compatible con importacion)')
|
|
189
|
+
.argument('<agentId>', 'ID del agente a exportar')
|
|
190
|
+
.option('-w, --workspace <id>', 'Workspace ID origen (sobreescribe config local)')
|
|
191
|
+
.option('-z, --zone <zone>', 'Zona LA o EU (sobreescribe config local)')
|
|
192
|
+
.option('-o, --output <path>', 'Ruta del archivo de salida (.json)')
|
|
193
|
+
.option('--no-channels', 'Excluir canales de la exportacion')
|
|
194
|
+
.option('--no-ai', 'Excluir configuracion de IA personalizada')
|
|
195
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
196
|
+
.action(async (agentId, options) => {
|
|
197
|
+
try {
|
|
198
|
+
const credentials = await (0, credentials_1.getStoredCredentials)();
|
|
199
|
+
const effectiveWorkspace = options.workspace || credentials.workspace;
|
|
200
|
+
const effectiveZone = (options.zone?.toUpperCase() === 'EU' ? 'EU' : options.zone?.toUpperCase() === 'LA' ? 'LA' : credentials.zone);
|
|
201
|
+
if (options.workspace || options.zone) {
|
|
202
|
+
console.log(chalk_1.default.hex('#FFA726')(`\n Modo soporte: workspace=${effectiveWorkspace} zona=${effectiveZone}`));
|
|
203
|
+
}
|
|
204
|
+
const agent = new plazbot_1.Agent({
|
|
205
|
+
workspaceId: effectiveWorkspace,
|
|
206
|
+
apiKey: credentials.apiKey,
|
|
207
|
+
zone: effectiveZone,
|
|
208
|
+
...(options.dev && { customUrl: 'http://localhost:5090' }),
|
|
209
|
+
});
|
|
210
|
+
const spinner = (0, ui_1.createSpinner)('Obteniendo agente...');
|
|
211
|
+
spinner.start();
|
|
212
|
+
const response = await agent.getAgentById({ id: agentId });
|
|
213
|
+
const agentData = response.agent || response.data || response;
|
|
214
|
+
spinner.succeed('Agente obtenido');
|
|
215
|
+
// Limpiar para exportacion
|
|
216
|
+
const cleaned = cleanAgentForExport(agentData);
|
|
217
|
+
// Aplicar opciones de exclusion
|
|
218
|
+
if (!options.channels) {
|
|
219
|
+
delete cleaned.channels;
|
|
220
|
+
}
|
|
221
|
+
if (!options.ai) {
|
|
222
|
+
cleaned.customAIConfig = false;
|
|
223
|
+
delete cleaned.aiProviders;
|
|
224
|
+
}
|
|
225
|
+
const jsonOutput = JSON.stringify(cleaned, null, 2);
|
|
226
|
+
// Guardar en archivo o mostrar en consola
|
|
227
|
+
if (options.output) {
|
|
228
|
+
const outputPath = path_1.default.resolve(options.output);
|
|
229
|
+
fs_1.default.writeFileSync(outputPath, jsonOutput, 'utf-8');
|
|
230
|
+
console.log(chalk_1.default.hex('#4ade80')(`\n Exportado a: ${outputPath}`));
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
console.log();
|
|
234
|
+
console.log(jsonOutput);
|
|
235
|
+
}
|
|
236
|
+
console.log();
|
|
237
|
+
console.log(chalk_1.default.gray(' Este JSON es compatible con:'));
|
|
238
|
+
console.log(chalk_1.default.gray(' - plazbot agent create <archivo.json>'));
|
|
239
|
+
console.log(chalk_1.default.gray(' - Importar JSON en la plataforma (AgentTraining)'));
|
|
240
|
+
console.log();
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
const message = error instanceof Error ? error.message : 'Error desconocido';
|
|
244
|
+
logger_1.logger.error(message);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
exports.crossCopyCommand = new commander_1.Command('cross-copy')
|
|
249
|
+
.description('Copiar un agente de un workspace a otro')
|
|
250
|
+
.argument('<agentId>', 'ID del agente a copiar')
|
|
251
|
+
.requiredOption('--from-workspace <id>', 'Workspace ID de origen')
|
|
252
|
+
.requiredOption('--to-workspace <id>', 'Workspace ID de destino')
|
|
253
|
+
.option('--from-zone <zone>', 'Zona del workspace origen (LA o EU)', 'LA')
|
|
254
|
+
.option('--to-zone <zone>', 'Zona del workspace destino (LA o EU)', 'LA')
|
|
255
|
+
.option('--no-channels', 'No copiar canales (recomendado)')
|
|
256
|
+
.option('--no-ai', 'No copiar configuracion de IA personalizada')
|
|
257
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
258
|
+
.action(async (agentId, options) => {
|
|
259
|
+
try {
|
|
260
|
+
const credentials = await (0, credentials_1.getStoredCredentials)();
|
|
261
|
+
const fromZone = (options.fromZone.toUpperCase() === 'EU' ? 'EU' : 'LA');
|
|
262
|
+
const toZone = (options.toZone.toUpperCase() === 'EU' ? 'EU' : 'LA');
|
|
263
|
+
console.log();
|
|
264
|
+
console.log(chalk_1.default.hex('#4CAF50')(' ┌' + '─'.repeat(58) + '┐'));
|
|
265
|
+
console.log(chalk_1.default.hex('#4CAF50')(' │') + chalk_1.default.bold(' Copiar agente entre workspaces').padEnd(68) + chalk_1.default.hex('#4CAF50')('│'));
|
|
266
|
+
console.log(chalk_1.default.hex('#4CAF50')(' └' + '─'.repeat(58) + '┘'));
|
|
267
|
+
console.log();
|
|
268
|
+
console.log(chalk_1.default.gray(` Origen: ${options.fromWorkspace} (${fromZone})`));
|
|
269
|
+
console.log(chalk_1.default.gray(` Destino: ${options.toWorkspace} (${toZone})`));
|
|
270
|
+
console.log(chalk_1.default.gray(` Agente: ${agentId}`));
|
|
271
|
+
console.log();
|
|
272
|
+
// 1. Obtener agente del workspace origen
|
|
273
|
+
const spinnerGet = (0, ui_1.createSpinner)('Obteniendo agente del workspace origen...');
|
|
274
|
+
spinnerGet.start();
|
|
275
|
+
const sourceAgent = new plazbot_1.Agent({
|
|
276
|
+
workspaceId: options.fromWorkspace,
|
|
277
|
+
apiKey: credentials.apiKey,
|
|
278
|
+
zone: fromZone,
|
|
279
|
+
...(options.dev && { customUrl: 'http://localhost:5090' }),
|
|
280
|
+
});
|
|
281
|
+
const response = await sourceAgent.getAgentById({ id: agentId });
|
|
282
|
+
const agentData = response.agent || response.data || response;
|
|
283
|
+
if (!agentData || !agentData.name) {
|
|
284
|
+
spinnerGet.fail('No se encontro el agente en el workspace origen');
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
spinnerGet.succeed(`Agente obtenido: ${agentData.name}`);
|
|
288
|
+
// 2. Limpiar JSON
|
|
289
|
+
const cleaned = cleanAgentForExport(agentData);
|
|
290
|
+
// Ajustar zona al destino
|
|
291
|
+
cleaned.zone = toZone;
|
|
292
|
+
// Limpiar canales (los canales son especificos del workspace)
|
|
293
|
+
if (!options.channels) {
|
|
294
|
+
cleaned.channels = [];
|
|
295
|
+
}
|
|
296
|
+
// Limpiar AI config si se solicita
|
|
297
|
+
if (!options.ai) {
|
|
298
|
+
cleaned.customAIConfig = false;
|
|
299
|
+
delete cleaned.aiProviders;
|
|
300
|
+
}
|
|
301
|
+
// Desactivar widget (seguridad)
|
|
302
|
+
cleaned.enableWidget = false;
|
|
303
|
+
// 3. Mostrar resumen
|
|
304
|
+
console.log();
|
|
305
|
+
console.log(chalk_1.default.bold(' Configuracion a copiar:'));
|
|
306
|
+
console.log(chalk_1.default.gray(` Nombre: ${cleaned.name}`));
|
|
307
|
+
console.log(chalk_1.default.gray(` Zona destino: ${cleaned.zone}`));
|
|
308
|
+
console.log(chalk_1.default.gray(` Canales: ${cleaned.channels?.length || 0}`));
|
|
309
|
+
console.log(chalk_1.default.gray(` Servicios: ${cleaned.services?.length || 0}`));
|
|
310
|
+
console.log(chalk_1.default.gray(` Acciones: ${cleaned.actions?.length || 0}`));
|
|
311
|
+
console.log(chalk_1.default.gray(` AI Custom: ${cleaned.customAIConfig ? 'Si' : 'No'}`));
|
|
312
|
+
console.log();
|
|
313
|
+
const { confirm } = await inquirer_1.default.prompt([{
|
|
314
|
+
type: 'confirm',
|
|
315
|
+
name: 'confirm',
|
|
316
|
+
message: 'Crear este agente en el workspace destino?',
|
|
317
|
+
default: true,
|
|
318
|
+
}]);
|
|
319
|
+
if (!confirm) {
|
|
320
|
+
console.log(chalk_1.default.gray(' Operacion cancelada.'));
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
// 4. Crear en workspace destino
|
|
324
|
+
const spinnerCreate = (0, ui_1.createSpinner)('Creando agente en workspace destino...');
|
|
325
|
+
spinnerCreate.start();
|
|
326
|
+
const targetAgent = new plazbot_1.Agent({
|
|
327
|
+
workspaceId: options.toWorkspace,
|
|
328
|
+
apiKey: credentials.apiKey,
|
|
329
|
+
zone: toZone,
|
|
330
|
+
...(options.dev && { customUrl: 'http://localhost:5090' }),
|
|
331
|
+
});
|
|
332
|
+
const result = await targetAgent.addAgent(cleaned);
|
|
333
|
+
if (result?.success === false) {
|
|
334
|
+
spinnerCreate.fail(`Error: ${result.message || 'No se pudo crear el agente'}`);
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
const newId = result?.agentId || result?.id || result?.data?.agentId;
|
|
338
|
+
spinnerCreate.succeed('Agente creado exitosamente');
|
|
339
|
+
console.log();
|
|
340
|
+
if (newId) {
|
|
341
|
+
console.log(chalk_1.default.hex('#4ade80')(` Nuevo ID: ${newId}`));
|
|
342
|
+
}
|
|
343
|
+
console.log(chalk_1.default.gray(' Widget deshabilitado por defecto.'));
|
|
344
|
+
console.log(chalk_1.default.gray(' Canales deben configurarse manualmente en el workspace destino.'));
|
|
345
|
+
console.log();
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
const message = error instanceof Error ? error.message : 'Error desconocido';
|
|
349
|
+
logger_1.logger.error(message);
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
@@ -17,6 +17,7 @@ const copy_1 = require("./copy");
|
|
|
17
17
|
const files_1 = require("./files");
|
|
18
18
|
const set_1 = require("./set");
|
|
19
19
|
const monitor_1 = require("./monitor");
|
|
20
|
+
const export_1 = require("./export");
|
|
20
21
|
exports.agentCommands = new commander_1.Command('agent')
|
|
21
22
|
.description('Comandos relacionados con agentes de IA')
|
|
22
23
|
.addCommand(list_1.listCommand)
|
|
@@ -33,4 +34,6 @@ exports.agentCommands = new commander_1.Command('agent')
|
|
|
33
34
|
.addCommand(copy_1.copyCommand)
|
|
34
35
|
.addCommand(files_1.filesCommand)
|
|
35
36
|
.addCommand(set_1.setCommand)
|
|
36
|
-
.addCommand(monitor_1.monitorCommand)
|
|
37
|
+
.addCommand(monitor_1.monitorCommand)
|
|
38
|
+
.addCommand(export_1.exportCommand)
|
|
39
|
+
.addCommand(export_1.crossCopyCommand);
|
package/package.json
CHANGED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { Agent } from 'plazbot';
|
|
3
|
+
import { getStoredCredentials } from '../../utils/credentials';
|
|
4
|
+
import { logger } from '../../utils/logger';
|
|
5
|
+
import { createSpinner } from '../../utils/ui';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import inquirer from 'inquirer';
|
|
10
|
+
|
|
11
|
+
// Campos validos segun el schema de agentValidation.ts (additionalProperties: false)
|
|
12
|
+
const VALID_AGENT_FIELDS = new Set([
|
|
13
|
+
'name', 'description', 'prompt', 'zone', 'timezone', 'buffer',
|
|
14
|
+
'color', 'question', 'enable', 'tags', 'showInChat',
|
|
15
|
+
'instructions', 'person', 'fallbacks', 'rules',
|
|
16
|
+
'examples', 'channels', 'services', 'actions',
|
|
17
|
+
'useToolCalling', 'customAIConfig', 'aiProviders',
|
|
18
|
+
'responseMode', 'batchWaitSeconds',
|
|
19
|
+
'enableWidget', 'enableWhatsappWidget', 'urlWhatsappWidget',
|
|
20
|
+
'formWidget', 'darkWidget', 'nameWidget', 'initialShowWidget',
|
|
21
|
+
'fUseAutomationFlowWidget', 'iconWidget',
|
|
22
|
+
'enableAvatarWidget', 'avatarGender', 'avatarVoiceId', 'avatarLanguage',
|
|
23
|
+
]);
|
|
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
|
+
|
|
107
|
+
function cleanAgentForExport(agentData: any): any {
|
|
108
|
+
const cleaned: any = {};
|
|
109
|
+
|
|
110
|
+
for (const key of Object.keys(agentData)) {
|
|
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;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Limpiar channels
|
|
144
|
+
if (cleaned.channels && Array.isArray(cleaned.channels)) {
|
|
145
|
+
cleaned.channels = cleaned.channels.map((ch: any) => ({
|
|
146
|
+
channel: ch.channel || '',
|
|
147
|
+
key: ch.key || '',
|
|
148
|
+
multianswer: ch.multianswer || false,
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Limpiar aiProviders
|
|
153
|
+
if (cleaned.aiProviders && Array.isArray(cleaned.aiProviders)) {
|
|
154
|
+
cleaned.aiProviders = cleaned.aiProviders.map((ai: any) => ({
|
|
155
|
+
provider: ai.provider,
|
|
156
|
+
model: ai.model,
|
|
157
|
+
apiToken: ai.apiToken || '',
|
|
158
|
+
temperature: ai.temperature,
|
|
159
|
+
maxTokens: ai.maxTokens,
|
|
160
|
+
isDefault: ai.isDefault,
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
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
|
+
|
|
172
|
+
return cleaned;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export const exportCommand = new Command('export')
|
|
176
|
+
.description('Exportar configuracion limpia de un agente (compatible con importacion)')
|
|
177
|
+
.argument('<agentId>', 'ID del agente a exportar')
|
|
178
|
+
.option('-w, --workspace <id>', 'Workspace ID origen (sobreescribe config local)')
|
|
179
|
+
.option('-z, --zone <zone>', 'Zona LA o EU (sobreescribe config local)')
|
|
180
|
+
.option('-o, --output <path>', 'Ruta del archivo de salida (.json)')
|
|
181
|
+
.option('--no-channels', 'Excluir canales de la exportacion')
|
|
182
|
+
.option('--no-ai', 'Excluir configuracion de IA personalizada')
|
|
183
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
184
|
+
.action(async (agentId: string, options: {
|
|
185
|
+
workspace?: string;
|
|
186
|
+
zone?: string;
|
|
187
|
+
output?: string;
|
|
188
|
+
channels: boolean;
|
|
189
|
+
ai: boolean;
|
|
190
|
+
dev: boolean;
|
|
191
|
+
}) => {
|
|
192
|
+
try {
|
|
193
|
+
const credentials = await getStoredCredentials();
|
|
194
|
+
|
|
195
|
+
const effectiveWorkspace = options.workspace || credentials.workspace;
|
|
196
|
+
const effectiveZone = (options.zone?.toUpperCase() === 'EU' ? 'EU' : options.zone?.toUpperCase() === 'LA' ? 'LA' : credentials.zone) as 'LA' | 'EU';
|
|
197
|
+
|
|
198
|
+
if (options.workspace || options.zone) {
|
|
199
|
+
console.log(chalk.hex('#FFA726')(`\n Modo soporte: workspace=${effectiveWorkspace} zona=${effectiveZone}`));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const agent = new Agent({
|
|
203
|
+
workspaceId: effectiveWorkspace,
|
|
204
|
+
apiKey: credentials.apiKey,
|
|
205
|
+
zone: effectiveZone,
|
|
206
|
+
...(options.dev && { customUrl: 'http://localhost:5090' }),
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const spinner = createSpinner('Obteniendo agente...');
|
|
210
|
+
spinner.start();
|
|
211
|
+
|
|
212
|
+
const response: any = await agent.getAgentById({ id: agentId });
|
|
213
|
+
const agentData = response.agent || response.data || response;
|
|
214
|
+
|
|
215
|
+
spinner.succeed('Agente obtenido');
|
|
216
|
+
|
|
217
|
+
// Limpiar para exportacion
|
|
218
|
+
const cleaned = cleanAgentForExport(agentData);
|
|
219
|
+
|
|
220
|
+
// Aplicar opciones de exclusion
|
|
221
|
+
if (!options.channels) {
|
|
222
|
+
delete cleaned.channels;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (!options.ai) {
|
|
226
|
+
cleaned.customAIConfig = false;
|
|
227
|
+
delete cleaned.aiProviders;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const jsonOutput = JSON.stringify(cleaned, null, 2);
|
|
231
|
+
|
|
232
|
+
// Guardar en archivo o mostrar en consola
|
|
233
|
+
if (options.output) {
|
|
234
|
+
const outputPath = path.resolve(options.output);
|
|
235
|
+
fs.writeFileSync(outputPath, jsonOutput, 'utf-8');
|
|
236
|
+
console.log(chalk.hex('#4ade80')(`\n Exportado a: ${outputPath}`));
|
|
237
|
+
} else {
|
|
238
|
+
console.log();
|
|
239
|
+
console.log(jsonOutput);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.log();
|
|
243
|
+
console.log(chalk.gray(' Este JSON es compatible con:'));
|
|
244
|
+
console.log(chalk.gray(' - plazbot agent create <archivo.json>'));
|
|
245
|
+
console.log(chalk.gray(' - Importar JSON en la plataforma (AgentTraining)'));
|
|
246
|
+
console.log();
|
|
247
|
+
|
|
248
|
+
} catch (error) {
|
|
249
|
+
const message = error instanceof Error ? error.message : 'Error desconocido';
|
|
250
|
+
logger.error(message);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
export const crossCopyCommand = new Command('cross-copy')
|
|
256
|
+
.description('Copiar un agente de un workspace a otro')
|
|
257
|
+
.argument('<agentId>', 'ID del agente a copiar')
|
|
258
|
+
.requiredOption('--from-workspace <id>', 'Workspace ID de origen')
|
|
259
|
+
.requiredOption('--to-workspace <id>', 'Workspace ID de destino')
|
|
260
|
+
.option('--from-zone <zone>', 'Zona del workspace origen (LA o EU)', 'LA')
|
|
261
|
+
.option('--to-zone <zone>', 'Zona del workspace destino (LA o EU)', 'LA')
|
|
262
|
+
.option('--no-channels', 'No copiar canales (recomendado)')
|
|
263
|
+
.option('--no-ai', 'No copiar configuracion de IA personalizada')
|
|
264
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
265
|
+
.action(async (agentId: string, options: {
|
|
266
|
+
fromWorkspace: string;
|
|
267
|
+
toWorkspace: string;
|
|
268
|
+
fromZone: string;
|
|
269
|
+
toZone: string;
|
|
270
|
+
channels: boolean;
|
|
271
|
+
ai: boolean;
|
|
272
|
+
dev: boolean;
|
|
273
|
+
}) => {
|
|
274
|
+
try {
|
|
275
|
+
const credentials = await getStoredCredentials();
|
|
276
|
+
const fromZone = (options.fromZone.toUpperCase() === 'EU' ? 'EU' : 'LA') as 'LA' | 'EU';
|
|
277
|
+
const toZone = (options.toZone.toUpperCase() === 'EU' ? 'EU' : 'LA') as 'LA' | 'EU';
|
|
278
|
+
|
|
279
|
+
console.log();
|
|
280
|
+
console.log(chalk.hex('#4CAF50')(' ┌' + '─'.repeat(58) + '┐'));
|
|
281
|
+
console.log(chalk.hex('#4CAF50')(' │') + chalk.bold(' Copiar agente entre workspaces').padEnd(68) + chalk.hex('#4CAF50')('│'));
|
|
282
|
+
console.log(chalk.hex('#4CAF50')(' └' + '─'.repeat(58) + '┘'));
|
|
283
|
+
console.log();
|
|
284
|
+
console.log(chalk.gray(` Origen: ${options.fromWorkspace} (${fromZone})`));
|
|
285
|
+
console.log(chalk.gray(` Destino: ${options.toWorkspace} (${toZone})`));
|
|
286
|
+
console.log(chalk.gray(` Agente: ${agentId}`));
|
|
287
|
+
console.log();
|
|
288
|
+
|
|
289
|
+
// 1. Obtener agente del workspace origen
|
|
290
|
+
const spinnerGet = createSpinner('Obteniendo agente del workspace origen...');
|
|
291
|
+
spinnerGet.start();
|
|
292
|
+
|
|
293
|
+
const sourceAgent = new Agent({
|
|
294
|
+
workspaceId: options.fromWorkspace,
|
|
295
|
+
apiKey: credentials.apiKey,
|
|
296
|
+
zone: fromZone,
|
|
297
|
+
...(options.dev && { customUrl: 'http://localhost:5090' }),
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const response: any = await sourceAgent.getAgentById({ id: agentId });
|
|
301
|
+
const agentData = response.agent || response.data || response;
|
|
302
|
+
|
|
303
|
+
if (!agentData || !agentData.name) {
|
|
304
|
+
spinnerGet.fail('No se encontro el agente en el workspace origen');
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
spinnerGet.succeed(`Agente obtenido: ${agentData.name}`);
|
|
309
|
+
|
|
310
|
+
// 2. Limpiar JSON
|
|
311
|
+
const cleaned = cleanAgentForExport(agentData);
|
|
312
|
+
|
|
313
|
+
// Ajustar zona al destino
|
|
314
|
+
cleaned.zone = toZone;
|
|
315
|
+
|
|
316
|
+
// Limpiar canales (los canales son especificos del workspace)
|
|
317
|
+
if (!options.channels) {
|
|
318
|
+
cleaned.channels = [];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Limpiar AI config si se solicita
|
|
322
|
+
if (!options.ai) {
|
|
323
|
+
cleaned.customAIConfig = false;
|
|
324
|
+
delete cleaned.aiProviders;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Desactivar widget (seguridad)
|
|
328
|
+
cleaned.enableWidget = false;
|
|
329
|
+
|
|
330
|
+
// 3. Mostrar resumen
|
|
331
|
+
console.log();
|
|
332
|
+
console.log(chalk.bold(' Configuracion a copiar:'));
|
|
333
|
+
console.log(chalk.gray(` Nombre: ${cleaned.name}`));
|
|
334
|
+
console.log(chalk.gray(` Zona destino: ${cleaned.zone}`));
|
|
335
|
+
console.log(chalk.gray(` Canales: ${cleaned.channels?.length || 0}`));
|
|
336
|
+
console.log(chalk.gray(` Servicios: ${cleaned.services?.length || 0}`));
|
|
337
|
+
console.log(chalk.gray(` Acciones: ${cleaned.actions?.length || 0}`));
|
|
338
|
+
console.log(chalk.gray(` AI Custom: ${cleaned.customAIConfig ? 'Si' : 'No'}`));
|
|
339
|
+
console.log();
|
|
340
|
+
|
|
341
|
+
const { confirm } = await (inquirer as any).prompt([{
|
|
342
|
+
type: 'confirm',
|
|
343
|
+
name: 'confirm',
|
|
344
|
+
message: 'Crear este agente en el workspace destino?',
|
|
345
|
+
default: true,
|
|
346
|
+
}]);
|
|
347
|
+
|
|
348
|
+
if (!confirm) {
|
|
349
|
+
console.log(chalk.gray(' Operacion cancelada.'));
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// 4. Crear en workspace destino
|
|
354
|
+
const spinnerCreate = createSpinner('Creando agente en workspace destino...');
|
|
355
|
+
spinnerCreate.start();
|
|
356
|
+
|
|
357
|
+
const targetAgent = new Agent({
|
|
358
|
+
workspaceId: options.toWorkspace,
|
|
359
|
+
apiKey: credentials.apiKey,
|
|
360
|
+
zone: toZone,
|
|
361
|
+
...(options.dev && { customUrl: 'http://localhost:5090' }),
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const result: any = await targetAgent.addAgent(cleaned);
|
|
365
|
+
|
|
366
|
+
if (result?.success === false) {
|
|
367
|
+
spinnerCreate.fail(`Error: ${result.message || 'No se pudo crear el agente'}`);
|
|
368
|
+
process.exit(1);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const newId = result?.agentId || result?.id || result?.data?.agentId;
|
|
372
|
+
spinnerCreate.succeed('Agente creado exitosamente');
|
|
373
|
+
|
|
374
|
+
console.log();
|
|
375
|
+
if (newId) {
|
|
376
|
+
console.log(chalk.hex('#4ade80')(` Nuevo ID: ${newId}`));
|
|
377
|
+
}
|
|
378
|
+
console.log(chalk.gray(' Widget deshabilitado por defecto.'));
|
|
379
|
+
console.log(chalk.gray(' Canales deben configurarse manualmente en el workspace destino.'));
|
|
380
|
+
console.log();
|
|
381
|
+
|
|
382
|
+
} catch (error) {
|
|
383
|
+
const message = error instanceof Error ? error.message : 'Error desconocido';
|
|
384
|
+
logger.error(message);
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
@@ -14,6 +14,7 @@ import { copyCommand } from './copy';
|
|
|
14
14
|
import { filesCommand } from './files';
|
|
15
15
|
import { setCommand } from './set';
|
|
16
16
|
import { monitorCommand } from './monitor';
|
|
17
|
+
import { exportCommand, crossCopyCommand } from './export';
|
|
17
18
|
|
|
18
19
|
export const agentCommands = new Command('agent')
|
|
19
20
|
.description('Comandos relacionados con agentes de IA')
|
|
@@ -31,4 +32,6 @@ export const agentCommands = new Command('agent')
|
|
|
31
32
|
.addCommand(copyCommand)
|
|
32
33
|
.addCommand(filesCommand)
|
|
33
34
|
.addCommand(setCommand)
|
|
34
|
-
.addCommand(monitorCommand)
|
|
35
|
+
.addCommand(monitorCommand)
|
|
36
|
+
.addCommand(exportCommand)
|
|
37
|
+
.addCommand(crossCopyCommand);
|