plazbot-cli 0.4.1 → 0.5.0
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.
- package/README.md +67 -111
- package/dist/cli.js +26 -11
- package/dist/commands/agent/ai-config.js +264 -63
- package/dist/commands/agent/chat.js +16 -11
- package/dist/commands/agent/copy.js +49 -13
- package/dist/commands/agent/create.js +62 -17
- package/dist/commands/agent/delete.js +50 -39
- package/dist/commands/agent/enable-widget.js +47 -27
- package/dist/commands/agent/export.js +179 -88
- package/dist/commands/agent/files.js +155 -73
- package/dist/commands/agent/get.js +24 -16
- package/dist/commands/agent/index.js +12 -10
- package/dist/commands/agent/list.js +20 -11
- package/dist/commands/agent/monitor.js +101 -53
- package/dist/commands/agent/on-message.js +55 -32
- package/dist/commands/agent/set.js +230 -82
- package/dist/commands/agent/templates.js +259 -156
- package/dist/commands/agent/tools.js +356 -123
- package/dist/commands/agent/update.js +69 -28
- package/dist/commands/agent/validate.js +72 -40
- package/dist/commands/agent/wizard.js +240 -329
- package/dist/commands/auth/browser-login.js +37 -31
- package/dist/commands/auth/login.js +49 -45
- package/dist/commands/auth/logout.js +20 -11
- package/dist/commands/auth/status.js +36 -12
- package/dist/commands/auth/workspace.js +61 -27
- package/dist/commands/portal/add-agent.js +106 -45
- package/dist/commands/portal/add-link.js +25 -28
- package/dist/commands/portal/clear-links.js +39 -19
- package/dist/commands/portal/create.js +66 -37
- package/dist/commands/portal/delete.js +40 -45
- package/dist/commands/portal/get.js +21 -54
- package/dist/commands/portal/index.js +12 -12
- package/dist/commands/portal/list.js +34 -52
- package/dist/commands/portal/shared.js +92 -0
- package/dist/commands/portal/update.js +100 -70
- package/dist/commands/whatsapp/broadcast.js +169 -77
- package/dist/commands/whatsapp/channels.js +167 -65
- package/dist/commands/whatsapp/chat.js +45 -46
- package/dist/commands/whatsapp/connect-flow.js +16 -15
- package/dist/commands/whatsapp/connect.js +36 -21
- package/dist/commands/whatsapp/delete-webhook.js +42 -25
- package/dist/commands/whatsapp/index.js +15 -16
- package/dist/commands/whatsapp/register-webhook.js +43 -26
- package/dist/commands/whatsapp/send-template.js +62 -61
- package/dist/commands/whatsapp/send.js +46 -33
- package/dist/commands/whatsapp/setup.js +62 -52
- package/dist/commands/whatsapp/shared.js +75 -0
- package/dist/commands/whatsapp/widget.js +82 -45
- package/dist/commands/workers/deploy.js +80 -58
- package/dist/commands/workers/index.js +8 -8
- package/dist/commands/workers/list.js +42 -20
- package/dist/commands/workers/logs.js +139 -36
- package/dist/commands/workers/remove.js +45 -47
- package/dist/commands/workers/secret.js +159 -83
- package/dist/commands/workers/test.js +108 -60
- package/dist/schemas/agent.config.schema.json +159 -8
- package/dist/studio/api/sseClient.js +1 -1
- package/dist/studio/api/studioApi.js +8 -0
- package/dist/studio/components/App.js +12 -0
- package/dist/studio/components/Footer.js +1 -1
- package/dist/studio/index.js +77 -14
- package/dist/studio/runOneShot.js +62 -23
- package/dist/studio/runRepl.js +5 -5
- package/dist/studio/slash/handlers.js +11 -8
- package/dist/studio/state/store.js +1 -1
- package/dist/utils/agent-errors.js +14 -11
- package/dist/utils/agent-patch.js +60 -0
- package/dist/utils/banner.js +9 -5
- package/dist/utils/confirm.js +31 -0
- package/dist/utils/credentials.js +13 -3
- package/dist/utils/logger.js +13 -10
- package/dist/utils/output.js +78 -0
- package/dist/utils/phone.js +22 -0
- package/dist/utils/ui.js +14 -11
- package/package.json +2 -2
|
@@ -1,26 +1,43 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
2
2
|
import { Agent } from 'plazbot';
|
|
3
3
|
import { getStoredCredentials } from '../../utils/credentials.js';
|
|
4
|
-
import { logger } from '../../utils/logger.js';
|
|
5
4
|
import { addExamples } from '../../utils/help.js';
|
|
6
5
|
import { createSpinner } from '../../utils/ui.js';
|
|
7
6
|
import chalk from 'chalk';
|
|
8
7
|
import fs from 'fs';
|
|
9
8
|
import path from 'path';
|
|
10
|
-
import
|
|
11
|
-
|
|
9
|
+
import { jsonOption, isJsonMode, printJson, fail, CliError } from '../../utils/output.js';
|
|
10
|
+
import { addYesOption, confirmAction } from '../../utils/confirm.js';
|
|
11
|
+
function parseZone(value, flag) {
|
|
12
|
+
const zone = value.toUpperCase();
|
|
13
|
+
if (zone !== 'LA' && zone !== 'EU') {
|
|
14
|
+
throw new CliError(`${flag} must be LA or EU (got "${value}").`, 'invalid_zone');
|
|
15
|
+
}
|
|
16
|
+
return zone;
|
|
17
|
+
}
|
|
18
|
+
// Top-level fields of the agent schema the API validates on create/update
|
|
19
|
+
// (Plazbot-Back-v2/Schemas/agent.config.schema.json, additionalProperties: false).
|
|
20
|
+
// /api/agent/update replaces the whole document, so every field the agent can have must be
|
|
21
|
+
// listed here or an update built from this config would wipe it. `version` is left out
|
|
22
|
+
// (the API manages it).
|
|
12
23
|
const VALID_AGENT_FIELDS = new Set([
|
|
13
24
|
'name', 'description', 'prompt', 'zone', 'timezone', 'buffer',
|
|
14
25
|
'color', 'question', 'enable', 'tags', 'showInChat',
|
|
15
26
|
'instructions', 'person', 'fallbacks', 'rules',
|
|
16
27
|
'examples', 'channels', 'services', 'actions', 'variables',
|
|
17
28
|
'useToolCalling', 'customAIConfig', 'aiProviders',
|
|
18
|
-
'responseMode', 'batchWaitSeconds',
|
|
29
|
+
'responseMode', 'batchWaitSeconds', 'enableWebSearch', 'enableContingency',
|
|
30
|
+
'enableAudioInput', 'imagePrompt', 'voiceConfig', 'source',
|
|
19
31
|
'enableWidget', 'enableWhatsappWidget', 'urlWhatsappWidget',
|
|
20
32
|
'formWidget', 'darkWidget', 'nameWidget', 'initialShowWidget',
|
|
21
|
-
'fUseAutomationFlowWidget', 'iconWidget',
|
|
33
|
+
'fUseAutomationFlowWidget', 'iconWidget', 'colorWidget', 'positionWidget',
|
|
34
|
+
'typeWidget', 'formTypeWidget', 'formFieldsWidget', 'initialViewWidget',
|
|
35
|
+
'multianswerWidget', 'enablePoweredBy', 'advisorNameWidget', 'advisorNameModeWidget',
|
|
22
36
|
'enableAvatarWidget', 'avatarGender', 'avatarVoiceId', 'avatarLanguage',
|
|
37
|
+
'avatarApiKey', 'avatarFaceId', 'avatarQuality', 'avatarBackground',
|
|
23
38
|
]);
|
|
39
|
+
/** Fields that hold third-party credentials: export/cross-copy only include them with --ai. */
|
|
40
|
+
export const SECRET_AGENT_FIELDS = ['aiProviders', 'avatarApiKey'];
|
|
24
41
|
// Campos que el schema espera como string (no pueden ser null)
|
|
25
42
|
const STRING_FIELDS = new Set([
|
|
26
43
|
'name', 'description', 'prompt', 'zone', 'timezone', 'color',
|
|
@@ -31,11 +48,27 @@ const STRING_FIELDS = new Set([
|
|
|
31
48
|
const ARRAY_FIELDS = new Set([
|
|
32
49
|
'tags', 'examples', 'channels', 'services', 'actions', 'aiProviders', 'variables',
|
|
33
50
|
]);
|
|
51
|
+
// Removes null/undefined recursively. Nested objects of the schema accept extra keys, so
|
|
52
|
+
// everything else the agent has is kept (an update built from this config must not lose data).
|
|
53
|
+
function dropNulls(value) {
|
|
54
|
+
if (Array.isArray(value))
|
|
55
|
+
return value.filter((v) => v !== null && v !== undefined).map(dropNulls);
|
|
56
|
+
if (value && typeof value === 'object') {
|
|
57
|
+
const out = {};
|
|
58
|
+
for (const [k, v] of Object.entries(value)) {
|
|
59
|
+
if (v !== null && v !== undefined)
|
|
60
|
+
out[k] = dropNulls(v);
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
34
66
|
function cleanRequiredFields(fields) {
|
|
35
67
|
if (!fields || !Array.isArray(fields))
|
|
36
68
|
return [];
|
|
37
69
|
return fields.map((f) => {
|
|
38
|
-
const clean =
|
|
70
|
+
const clean = dropNulls(f);
|
|
71
|
+
delete clean.properties;
|
|
39
72
|
if (f.name)
|
|
40
73
|
clean.name = f.name;
|
|
41
74
|
if (f.description)
|
|
@@ -81,7 +114,7 @@ function cleanServices(services) {
|
|
|
81
114
|
clean.responseMessage = svc.responseMessage;
|
|
82
115
|
if (svc.responseConditions && Array.isArray(svc.responseConditions)) {
|
|
83
116
|
clean.responseConditions = svc.responseConditions.map((rc) => {
|
|
84
|
-
const c = { condition: rc.condition || '' };
|
|
117
|
+
const c = { ...dropNulls(rc), condition: rc.condition || '' };
|
|
85
118
|
if (typeof rc.message === 'string')
|
|
86
119
|
c.message = rc.message;
|
|
87
120
|
if (typeof rc.nextService === 'string')
|
|
@@ -103,6 +136,7 @@ function cleanActions(actions) {
|
|
|
103
136
|
reference: act.reference || '',
|
|
104
137
|
enabled: act.enabled ?? true,
|
|
105
138
|
action: Array.isArray(act.action) ? act.action.map((a) => ({
|
|
139
|
+
...dropNulls(a),
|
|
106
140
|
type: a.type || '',
|
|
107
141
|
value: a.value ?? '',
|
|
108
142
|
})) : [],
|
|
@@ -114,6 +148,8 @@ function cleanActions(actions) {
|
|
|
114
148
|
clean.responseMessage = act.responseMessage;
|
|
115
149
|
if (typeof act.responseJson === 'boolean')
|
|
116
150
|
clean.responseJson = act.responseJson;
|
|
151
|
+
if (typeof act.responseExact === 'boolean')
|
|
152
|
+
clean.responseExact = act.responseExact;
|
|
117
153
|
return clean;
|
|
118
154
|
});
|
|
119
155
|
}
|
|
@@ -137,7 +173,7 @@ function cleanVariables(variables) {
|
|
|
137
173
|
return clean;
|
|
138
174
|
});
|
|
139
175
|
}
|
|
140
|
-
function cleanAgentForExport(agentData) {
|
|
176
|
+
export function cleanAgentForExport(agentData) {
|
|
141
177
|
const cleaned = {};
|
|
142
178
|
for (const key of Object.keys(agentData)) {
|
|
143
179
|
if (!VALID_AGENT_FIELDS.has(key))
|
|
@@ -207,17 +243,22 @@ export const exportCommand = new Command('export')
|
|
|
207
243
|
.argument('<agentId>', 'Agent ID to export')
|
|
208
244
|
.option('-w, --workspace <id>', 'Source workspace ID (overrides local config)')
|
|
209
245
|
.option('-z, --zone <zone>', 'Zone LA or EU (overrides local config)')
|
|
210
|
-
.option('-o, --output <path>', '
|
|
211
|
-
.option('--
|
|
212
|
-
.option('--
|
|
246
|
+
.option('-o, --output <path>', 'Write the JSON to this file instead of stdout')
|
|
247
|
+
.option('--channels', 'Include channels (workspace-specific)', false)
|
|
248
|
+
.option('--ai', 'Include the custom AI configuration and the avatar key (contains third-party API keys)', false)
|
|
249
|
+
// Compatibilidad: antes canales e IA se exportaban por defecto y se excluian con --no-*
|
|
250
|
+
.addOption(new Option('--no-channels').hideHelp())
|
|
251
|
+
.addOption(new Option('--no-ai').hideHelp())
|
|
252
|
+
.addOption(jsonOption('With -o, print {"output": "<path>"}; without -o the agent JSON is printed anyway'))
|
|
213
253
|
.option('--dev', 'Use development environment', false)
|
|
214
254
|
.action(async (agentId, options) => {
|
|
215
255
|
try {
|
|
216
256
|
const credentials = await getStoredCredentials();
|
|
217
257
|
const effectiveWorkspace = options.workspace || credentials.workspace;
|
|
218
|
-
const effectiveZone =
|
|
258
|
+
const effectiveZone = options.zone ? parseZone(options.zone, '--zone') : credentials.zone;
|
|
219
259
|
if (options.workspace || options.zone) {
|
|
220
|
-
|
|
260
|
+
// stderr: stdout queda solo para el JSON
|
|
261
|
+
console.error(chalk.hex('#FFA726')(`\n Support mode: workspace=${effectiveWorkspace} zone=${effectiveZone}`));
|
|
221
262
|
}
|
|
222
263
|
const agent = new Agent({
|
|
223
264
|
workspaceId: effectiveWorkspace,
|
|
@@ -227,51 +268,72 @@ export const exportCommand = new Command('export')
|
|
|
227
268
|
});
|
|
228
269
|
const spinner = createSpinner('Fetching agent...');
|
|
229
270
|
spinner.start();
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
271
|
+
let agentData;
|
|
272
|
+
try {
|
|
273
|
+
const response = await agent.getAgentById({ id: agentId });
|
|
274
|
+
agentData = response?.agent || response?.data || response;
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
spinner.fail('Could not fetch the agent');
|
|
278
|
+
throw err;
|
|
279
|
+
}
|
|
280
|
+
if (!agentData || !agentData.name) {
|
|
281
|
+
spinner.fail('Agent not found');
|
|
282
|
+
throw new CliError(`Agent ${agentId} was not found in workspace ${effectiveWorkspace}.`, 'not_found', "Check the ID with 'plazbot agent list'.");
|
|
283
|
+
}
|
|
284
|
+
spinner.succeed(`Agent fetched: ${agentData.name}`);
|
|
233
285
|
// Limpiar para exportacion
|
|
234
286
|
const cleaned = cleanAgentForExport(agentData);
|
|
235
|
-
//
|
|
287
|
+
// Canales e IA solo con --channels / --ai: la config de IA lleva los apiToken de los
|
|
288
|
+
// proveedores. Se omiten las claves (no se ponen en false) para que un
|
|
289
|
+
// export -> editar -> update no borre los canales ni la IA del agente.
|
|
236
290
|
if (!options.channels) {
|
|
237
291
|
delete cleaned.channels;
|
|
238
292
|
}
|
|
239
293
|
if (!options.ai) {
|
|
240
|
-
cleaned.customAIConfig
|
|
241
|
-
delete cleaned
|
|
294
|
+
delete cleaned.customAIConfig;
|
|
295
|
+
SECRET_AGENT_FIELDS.forEach((field) => delete cleaned[field]);
|
|
242
296
|
}
|
|
243
297
|
const jsonOutput = JSON.stringify(cleaned, null, 2);
|
|
244
298
|
// Guardar en archivo o mostrar en consola
|
|
245
299
|
if (options.output) {
|
|
246
300
|
const outputPath = path.resolve(options.output);
|
|
247
|
-
fs.writeFileSync(outputPath, jsonOutput, 'utf-8');
|
|
301
|
+
fs.writeFileSync(outputPath, jsonOutput + '\n', 'utf-8');
|
|
302
|
+
if (isJsonMode()) {
|
|
303
|
+
printJson({ output: outputPath, id: agentId, name: cleaned.name ?? null });
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
248
306
|
console.log(chalk.hex('#4ade80')(`\n Exported to: ${outputPath}`));
|
|
249
307
|
}
|
|
250
308
|
else {
|
|
251
|
-
|
|
309
|
+
// stdout solo lleva el JSON, para poder redirigirlo (export > agent.json)
|
|
252
310
|
console.log(jsonOutput);
|
|
311
|
+
if (isJsonMode())
|
|
312
|
+
return;
|
|
253
313
|
}
|
|
254
|
-
console.
|
|
255
|
-
console.
|
|
256
|
-
console.
|
|
257
|
-
console.
|
|
258
|
-
console.
|
|
314
|
+
console.error();
|
|
315
|
+
console.error(chalk.gray(' This JSON is compatible with:'));
|
|
316
|
+
console.error(chalk.gray(' - plazbot agent create <file.json>'));
|
|
317
|
+
console.error(chalk.gray(' - plazbot agent update <agentId> <file.json>'));
|
|
318
|
+
console.error(chalk.gray(' - Importing JSON in the platform (AgentTraining)'));
|
|
319
|
+
if (!options.channels || !options.ai) {
|
|
320
|
+
console.error(chalk.gray(` Not included: ${[!options.channels && 'channels (--channels)', !options.ai && 'AI configuration (--ai)'].filter(Boolean).join(', ')}`));
|
|
321
|
+
}
|
|
322
|
+
console.error();
|
|
259
323
|
}
|
|
260
324
|
catch (error) {
|
|
261
|
-
|
|
262
|
-
logger.error(message);
|
|
263
|
-
process.exit(1);
|
|
325
|
+
fail(error);
|
|
264
326
|
}
|
|
265
327
|
});
|
|
266
328
|
addExamples(exportCommand, [
|
|
267
329
|
{ description: 'Print the cleaned JSON to stdout',
|
|
268
|
-
command: 'plazbot agent export
|
|
330
|
+
command: 'plazbot agent export age_AbcDef123' },
|
|
269
331
|
{ description: 'Write the cleaned JSON to a file',
|
|
270
|
-
command: 'plazbot agent export
|
|
271
|
-
{ description: '
|
|
272
|
-
command: 'plazbot agent export
|
|
332
|
+
command: 'plazbot agent export age_AbcDef123 -o ./agent.json' },
|
|
333
|
+
{ description: 'Include channels and the AI configuration (contains provider API tokens)',
|
|
334
|
+
command: 'plazbot agent export age_AbcDef123 -o ./agent.json --channels --ai' },
|
|
273
335
|
{ description: 'Export from a different workspace (support mode)',
|
|
274
|
-
command: 'plazbot agent export
|
|
336
|
+
command: 'plazbot agent export age_AbcDef123 -w wok_Other123 -z LA -o ./agent.json' },
|
|
275
337
|
]);
|
|
276
338
|
export const crossCopyCommand = new Command('cross-copy')
|
|
277
339
|
.description('Copy an agent from one workspace to another')
|
|
@@ -280,23 +342,29 @@ export const crossCopyCommand = new Command('cross-copy')
|
|
|
280
342
|
.requiredOption('--to-workspace <id>', 'Target workspace ID')
|
|
281
343
|
.option('--from-zone <zone>', 'Source workspace zone (LA or EU)', 'LA')
|
|
282
344
|
.option('--to-zone <zone>', 'Target workspace zone (LA or EU)', 'LA')
|
|
283
|
-
.option('--
|
|
284
|
-
.option('--
|
|
285
|
-
.
|
|
286
|
-
.
|
|
345
|
+
.option('--channels', 'Also copy channels (usually wrong: channels belong to the source workspace)', false)
|
|
346
|
+
.option('--ai', 'Also copy the custom AI configuration and the avatar key (contains third-party API keys)', false)
|
|
347
|
+
.addOption(new Option('--no-channels').hideHelp())
|
|
348
|
+
.addOption(new Option('--no-ai').hideHelp())
|
|
349
|
+
.addOption(jsonOption('Print the result as JSON ({"id","sourceId","fromWorkspace","toWorkspace"})'))
|
|
350
|
+
.option('--dev', 'Use development environment', false);
|
|
351
|
+
addYesOption(crossCopyCommand);
|
|
352
|
+
crossCopyCommand.action(async (agentId, options) => {
|
|
287
353
|
try {
|
|
288
354
|
const credentials = await getStoredCredentials();
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
355
|
+
// En --json todo lo informativo va a stderr
|
|
356
|
+
const out = (message = '') => (isJsonMode() ? console.error : console.log)(message);
|
|
357
|
+
const fromZone = parseZone(options.fromZone, '--from-zone');
|
|
358
|
+
const toZone = parseZone(options.toZone, '--to-zone');
|
|
359
|
+
out();
|
|
360
|
+
out(chalk.hex('#4CAF50')(' ┌' + '─'.repeat(58) + '┐'));
|
|
361
|
+
out(chalk.hex('#4CAF50')(' │') + chalk.bold(' Copy agent between workspaces').padEnd(68) + chalk.hex('#4CAF50')('│'));
|
|
362
|
+
out(chalk.hex('#4CAF50')(' └' + '─'.repeat(58) + '┘'));
|
|
363
|
+
out();
|
|
364
|
+
out(chalk.gray(` Source: ${options.fromWorkspace} (${fromZone})`));
|
|
365
|
+
out(chalk.gray(` Target: ${options.toWorkspace} (${toZone})`));
|
|
366
|
+
out(chalk.gray(` Agent: ${agentId}`));
|
|
367
|
+
out();
|
|
300
368
|
// 1. Obtener agente del workspace origen
|
|
301
369
|
const spinnerGet = createSpinner('Fetching agent from source workspace...');
|
|
302
370
|
spinnerGet.start();
|
|
@@ -306,11 +374,18 @@ export const crossCopyCommand = new Command('cross-copy')
|
|
|
306
374
|
zone: fromZone,
|
|
307
375
|
...(options.dev && { customUrl: 'http://localhost:5090' }),
|
|
308
376
|
});
|
|
309
|
-
|
|
310
|
-
|
|
377
|
+
let agentData;
|
|
378
|
+
try {
|
|
379
|
+
const response = await sourceAgent.getAgentById({ id: agentId });
|
|
380
|
+
agentData = response?.agent || response?.data || response;
|
|
381
|
+
}
|
|
382
|
+
catch (err) {
|
|
383
|
+
spinnerGet.fail('Could not fetch the agent from the source workspace');
|
|
384
|
+
throw err;
|
|
385
|
+
}
|
|
311
386
|
if (!agentData || !agentData.name) {
|
|
312
|
-
spinnerGet.fail('Agent not found in source workspace');
|
|
313
|
-
|
|
387
|
+
spinnerGet.fail('Agent not found in the source workspace');
|
|
388
|
+
throw new CliError(`Agent ${agentId} was not found in workspace ${options.fromWorkspace} (${fromZone}).`, 'not_found', 'Check --from-workspace and --from-zone.');
|
|
314
389
|
}
|
|
315
390
|
spinnerGet.succeed(`Agent fetched: ${agentData.name}`);
|
|
316
391
|
// 2. Limpiar JSON
|
|
@@ -324,29 +399,24 @@ export const crossCopyCommand = new Command('cross-copy')
|
|
|
324
399
|
// Limpiar AI config si se solicita
|
|
325
400
|
if (!options.ai) {
|
|
326
401
|
cleaned.customAIConfig = false;
|
|
327
|
-
delete cleaned
|
|
402
|
+
SECRET_AGENT_FIELDS.forEach((field) => delete cleaned[field]);
|
|
328
403
|
}
|
|
329
404
|
// Desactivar widget (seguridad)
|
|
330
405
|
cleaned.enableWidget = false;
|
|
331
406
|
// 3. Mostrar resumen
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
default: true,
|
|
346
|
-
}]);
|
|
347
|
-
if (!confirm) {
|
|
348
|
-
console.log(chalk.gray(' Operation cancelled.'));
|
|
349
|
-
return;
|
|
407
|
+
out();
|
|
408
|
+
out(chalk.bold(' Configuration to copy:'));
|
|
409
|
+
out(chalk.gray(` Name: ${cleaned.name}`));
|
|
410
|
+
out(chalk.gray(` Target zone: ${cleaned.zone}`));
|
|
411
|
+
out(chalk.gray(` Channels: ${cleaned.channels?.length || 0}`));
|
|
412
|
+
out(chalk.gray(` Services: ${cleaned.services?.length || 0}`));
|
|
413
|
+
out(chalk.gray(` Actions: ${cleaned.actions?.length || 0}`));
|
|
414
|
+
out(chalk.gray(` Custom AI: ${cleaned.customAIConfig ? 'Yes' : 'No'}`));
|
|
415
|
+
out();
|
|
416
|
+
const ok = await confirmAction(`Create "${cleaned.name}" in workspace ${options.toWorkspace} (${toZone})?`, options);
|
|
417
|
+
if (!ok) {
|
|
418
|
+
out(chalk.gray(' Aborted.'));
|
|
419
|
+
process.exit(1);
|
|
350
420
|
}
|
|
351
421
|
// 4. Crear en workspace destino
|
|
352
422
|
const spinnerCreate = createSpinner('Creating agent in target workspace...');
|
|
@@ -357,32 +427,53 @@ export const crossCopyCommand = new Command('cross-copy')
|
|
|
357
427
|
zone: toZone,
|
|
358
428
|
...(options.dev && { customUrl: 'http://localhost:5090' }),
|
|
359
429
|
});
|
|
360
|
-
|
|
430
|
+
let result;
|
|
431
|
+
try {
|
|
432
|
+
result = await targetAgent.addAgent(cleaned);
|
|
433
|
+
}
|
|
434
|
+
catch (err) {
|
|
435
|
+
spinnerCreate.fail('Could not create the agent in the target workspace');
|
|
436
|
+
throw err;
|
|
437
|
+
}
|
|
361
438
|
if (result?.success === false) {
|
|
362
|
-
spinnerCreate.fail(
|
|
363
|
-
|
|
439
|
+
spinnerCreate.fail('Could not create the agent in the target workspace');
|
|
440
|
+
throw new CliError(result.message || 'The API rejected the agent configuration.', 'create_failed');
|
|
364
441
|
}
|
|
365
442
|
const newId = result?.agentId || result?.id || result?.data?.agentId;
|
|
366
443
|
spinnerCreate.succeed('Agent created successfully');
|
|
367
|
-
|
|
444
|
+
if (isJsonMode()) {
|
|
445
|
+
printJson({
|
|
446
|
+
id: newId ?? null,
|
|
447
|
+
sourceId: agentId,
|
|
448
|
+
name: cleaned.name ?? null,
|
|
449
|
+
fromWorkspace: options.fromWorkspace,
|
|
450
|
+
toWorkspace: options.toWorkspace,
|
|
451
|
+
fromZone,
|
|
452
|
+
toZone,
|
|
453
|
+
});
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
out();
|
|
368
457
|
if (newId) {
|
|
369
|
-
|
|
458
|
+
out(chalk.hex('#4ade80')(` New ID: ${newId}`));
|
|
459
|
+
}
|
|
460
|
+
out(chalk.gray(' Widget disabled by default.'));
|
|
461
|
+
if (!options.channels) {
|
|
462
|
+
out(chalk.gray(' Channels were not copied: connect them in the target workspace.'));
|
|
370
463
|
}
|
|
371
|
-
|
|
372
|
-
console.log(chalk.gray(' Channels must be configured manually in the target workspace.'));
|
|
373
|
-
console.log();
|
|
464
|
+
out();
|
|
374
465
|
}
|
|
375
466
|
catch (error) {
|
|
376
|
-
|
|
377
|
-
logger.error(message);
|
|
378
|
-
process.exit(1);
|
|
467
|
+
fail(error);
|
|
379
468
|
}
|
|
380
469
|
});
|
|
381
470
|
addExamples(crossCopyCommand, [
|
|
382
|
-
{ description: 'Copy an agent between two LA workspaces (
|
|
383
|
-
command: 'plazbot agent cross-copy
|
|
471
|
+
{ description: 'Copy an agent between two LA workspaces (without channels nor AI config)',
|
|
472
|
+
command: 'plazbot agent cross-copy age_AbcDef123 --from-workspace wok_Source123 --to-workspace wok_Target456' },
|
|
384
473
|
{ description: 'Copy across zones (LA → EU)',
|
|
385
|
-
command: 'plazbot agent cross-copy
|
|
474
|
+
command: 'plazbot agent cross-copy age_AbcDef123 --from-workspace wok_Source123 --from-zone LA --to-workspace wok_Target456 --to-zone EU' },
|
|
475
|
+
{ description: 'Copy without the confirmation prompt (scripts and CI)',
|
|
476
|
+
command: 'plazbot agent cross-copy age_AbcDef123 --from-workspace wok_Source123 --to-workspace wok_Target456 --yes --json' },
|
|
386
477
|
{ description: 'Copy preserving channels and AI providers',
|
|
387
|
-
command: 'plazbot agent cross-copy
|
|
478
|
+
command: 'plazbot agent cross-copy age_AbcDef123 --from-workspace wok_Source123 --to-workspace wok_Target456 --channels --ai' },
|
|
388
479
|
]);
|