plazbot-cli 0.3.7 → 0.4.1

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.
Files changed (117) hide show
  1. package/README.md +38 -2
  2. package/dist/commands/auth/browser-login.js +121 -0
  3. package/dist/commands/auth/index.js +5 -1
  4. package/dist/commands/auth/login.js +1 -3
  5. package/dist/commands/auth/logout.js +4 -3
  6. package/dist/commands/auth/status.js +8 -5
  7. package/dist/commands/auth/workspace.js +73 -0
  8. package/dist/commands/whatsapp/connect-flow.js +137 -0
  9. package/dist/commands/whatsapp/connect.js +26 -160
  10. package/dist/commands/whatsapp/index.js +4 -0
  11. package/dist/commands/whatsapp/setup.js +242 -0
  12. package/dist/schemas/agent.config.schema.json +24 -0
  13. package/dist/studio/components/App.js +1 -1
  14. package/dist/studio/runOneShot.js +2 -2
  15. package/dist/studio/runRepl.js +1 -1
  16. package/dist/utils/agent-errors.js +2 -2
  17. package/dist/utils/banner.js +10 -9
  18. package/dist/utils/credentials.js +31 -3
  19. package/dist/utils/open-browser.js +17 -0
  20. package/package.json +14 -6
  21. package/.claude/settings.local.json +0 -19
  22. package/CLAUDE.md +0 -121
  23. package/agent.config.json +0 -21
  24. package/dist/studio/components/AgentPanel.js +0 -35
  25. package/dist/studio/render/json.js +0 -16
  26. package/dist/studio/whatsapp/api.js +0 -63
  27. package/dist/studio/whatsapp/polling.js +0 -77
  28. package/dist/studio/whatsapp/types.js +0 -31
  29. package/src/cli.ts +0 -57
  30. package/src/commands/agent/ai-config.ts +0 -164
  31. package/src/commands/agent/chat.ts +0 -225
  32. package/src/commands/agent/copy.ts +0 -46
  33. package/src/commands/agent/create.ts +0 -103
  34. package/src/commands/agent/delete.ts +0 -71
  35. package/src/commands/agent/enable-widget.ts +0 -67
  36. package/src/commands/agent/export.ts +0 -430
  37. package/src/commands/agent/files.ts +0 -171
  38. package/src/commands/agent/get.ts +0 -233
  39. package/src/commands/agent/index.ts +0 -55
  40. package/src/commands/agent/list.ts +0 -58
  41. package/src/commands/agent/monitor.ts +0 -375
  42. package/src/commands/agent/on-message.ts +0 -82
  43. package/src/commands/agent/set.ts +0 -149
  44. package/src/commands/agent/templates.ts +0 -892
  45. package/src/commands/agent/tools.ts +0 -168
  46. package/src/commands/agent/update.ts +0 -61
  47. package/src/commands/agent/validate.ts +0 -135
  48. package/src/commands/agent/wizard.ts +0 -756
  49. package/src/commands/auth/index.ts +0 -10
  50. package/src/commands/auth/login.ts +0 -72
  51. package/src/commands/auth/logout.ts +0 -28
  52. package/src/commands/auth/status.ts +0 -28
  53. package/src/commands/portal/add-agent.ts +0 -66
  54. package/src/commands/portal/add-link.ts +0 -47
  55. package/src/commands/portal/clear-links.ts +0 -33
  56. package/src/commands/portal/create.ts +0 -58
  57. package/src/commands/portal/delete.ts +0 -70
  58. package/src/commands/portal/get.ts +0 -77
  59. package/src/commands/portal/index.ts +0 -32
  60. package/src/commands/portal/list.ts +0 -79
  61. package/src/commands/portal/update.ts +0 -93
  62. package/src/commands/whatsapp/broadcast.ts +0 -110
  63. package/src/commands/whatsapp/channels.ts +0 -109
  64. package/src/commands/whatsapp/chat.ts +0 -85
  65. package/src/commands/whatsapp/connect.ts +0 -220
  66. package/src/commands/whatsapp/delete-webhook.ts +0 -43
  67. package/src/commands/whatsapp/index.ts +0 -37
  68. package/src/commands/whatsapp/register-webhook.ts +0 -47
  69. package/src/commands/whatsapp/send-template.ts +0 -94
  70. package/src/commands/whatsapp/send.ts +0 -55
  71. package/src/commands/whatsapp/widget.ts +0 -75
  72. package/src/commands/workers/deploy.ts +0 -156
  73. package/src/commands/workers/index.ts +0 -30
  74. package/src/commands/workers/list.ts +0 -105
  75. package/src/commands/workers/logs.ts +0 -99
  76. package/src/commands/workers/remove.ts +0 -103
  77. package/src/commands/workers/secret.ts +0 -191
  78. package/src/commands/workers/test.ts +0 -145
  79. package/src/schemas/agent.config.schema.json +0 -569
  80. package/src/studio/api/sseClient.ts +0 -91
  81. package/src/studio/api/studioApi.ts +0 -27
  82. package/src/studio/api/types.ts +0 -96
  83. package/src/studio/components/App.tsx +0 -274
  84. package/src/studio/components/ChatLog.tsx +0 -121
  85. package/src/studio/components/ConnectCard.tsx +0 -171
  86. package/src/studio/components/Footer.tsx +0 -38
  87. package/src/studio/components/Header.tsx +0 -39
  88. package/src/studio/components/Input.tsx +0 -176
  89. package/src/studio/components/Message.tsx +0 -87
  90. package/src/studio/components/Suggestions.tsx +0 -26
  91. package/src/studio/components/ToolCall.tsx +0 -58
  92. package/src/studio/connect/api.ts +0 -98
  93. package/src/studio/connect/polling.ts +0 -94
  94. package/src/studio/connect/types.ts +0 -141
  95. package/src/studio/index.ts +0 -58
  96. package/src/studio/render/markdown.ts +0 -93
  97. package/src/studio/render/steps.ts +0 -57
  98. package/src/studio/runOneShot.ts +0 -114
  99. package/src/studio/runRepl.tsx +0 -82
  100. package/src/studio/slash/handlers.ts +0 -226
  101. package/src/studio/slash/parser.ts +0 -41
  102. package/src/studio/slash/registry.ts +0 -54
  103. package/src/studio/state/store.ts +0 -279
  104. package/src/types/agent.ts +0 -63
  105. package/src/types/auth.ts +0 -9
  106. package/src/types/common.ts +0 -10
  107. package/src/types/message.ts +0 -34
  108. package/src/types/portal.ts +0 -56
  109. package/src/types/workers.ts +0 -86
  110. package/src/utils/agent-errors.ts +0 -67
  111. package/src/utils/api.ts +0 -35
  112. package/src/utils/banner.ts +0 -99
  113. package/src/utils/credentials.ts +0 -38
  114. package/src/utils/help.ts +0 -51
  115. package/src/utils/logger.ts +0 -40
  116. package/src/utils/ui.ts +0 -109
  117. package/tsconfig.json +0 -18
@@ -1,756 +0,0 @@
1
- import inquirer from 'inquirer';
2
- import chalk from 'chalk';
3
- import axios from 'axios';
4
- import ora from 'ora';
5
- import { theme, section, kvPair } from '../../utils/ui.js';
6
-
7
- interface AgentConfig {
8
- name: string;
9
- description?: string;
10
- prompt: string;
11
- buffer: number;
12
- zone: string;
13
- color: string;
14
- useToolCalling: boolean;
15
- customAIConfig: boolean;
16
- aiProviders: AIProvider[];
17
- instructions: AgentInstructions;
18
- person: AgentPerson;
19
- fallbacks: AgentFallbacks;
20
- services: AgentService[];
21
- actions: AgentAction[];
22
- channels: AgentChannel[];
23
- examples: AgentExample[];
24
- tags: string[];
25
- }
26
-
27
- interface AIProvider {
28
- provider: string;
29
- model: string;
30
- apiToken: string;
31
- temperature: number;
32
- maxTokens: number;
33
- isDefault: boolean;
34
- }
35
-
36
- interface AgentInstructions {
37
- tone: string;
38
- style: string;
39
- personality: string;
40
- objective: string;
41
- language: string;
42
- emojis: boolean;
43
- }
44
-
45
- interface AgentPerson {
46
- name: string;
47
- role: string;
48
- speaksInFirstPerson: boolean;
49
- }
50
-
51
- interface AgentFallbacks {
52
- noAnswer: string;
53
- serviceError: string;
54
- doNotUnderstand: string;
55
- }
56
-
57
- interface AgentService {
58
- intent: string;
59
- reference: string;
60
- enabled: boolean;
61
- method: string;
62
- endpoint: string;
63
- requiredFields: { name: string; type: string; description: string; promptHint: string }[];
64
- headers: Record<string, string>;
65
- bodyTemplate: Record<string, string>;
66
- responseMapping: Record<string, string>;
67
- responseMessage: string;
68
- responseConditions: { condition: string; message: string }[];
69
- }
70
-
71
- interface AgentAction {
72
- intent: string;
73
- reference: string;
74
- enabled: boolean;
75
- requiredFields: { name: string; type: string; description: string; promptHint: string }[];
76
- responseMessage: string;
77
- action: { type: string; value: string }[];
78
- }
79
-
80
- interface AgentChannel {
81
- channel: string;
82
- key: string;
83
- multianswer: boolean;
84
- }
85
-
86
- interface AgentExample {
87
- value: string;
88
- color: string;
89
- }
90
-
91
- const MODELS: Record<string, string[]> = {
92
- openai: ['gpt-4o', 'gpt-4o-mini', 'gpt-4', 'gpt-3.5-turbo', 'o1-preview', 'o1-mini'],
93
- claude: ['claude-3-5-sonnet-20241022', 'claude-3-opus-20240229', 'claude-3-haiku-20240307'],
94
- gemini: ['gemini-2.0-flash', 'gemini-1.5-pro', 'gemini-1.5-flash'],
95
- };
96
-
97
- const COLORS = ['blue', 'green', 'orange', 'gray', 'white'];
98
-
99
- export 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
- { name: 'Telegram', value: 'telegram' },
117
- ];
118
-
119
- const CHANNEL_AGENT_MAP: Record<string, string> = {
120
- whatsapp: 'whatsapp',
121
- whatsapp_business: 'whatsapp',
122
- instagram: 'instagram',
123
- facebook: 'messenger',
124
- telegram: 'telegram',
125
- };
126
-
127
- function getIntegrationLabel(ig: any): string {
128
- if (ig.type === 'whatsapp') {
129
- return ig.aliasName
130
- ? `${ig.aliasName} (${ig.cellphoneNumberFormat || ig.cellphoneNumber || ''})`
131
- : ig.cellphoneNumberFormat || ig.cellphoneNumber || ig.id;
132
- }
133
- return ig.aliasName || ig.srcName || ig.id;
134
- }
135
-
136
- function getIntegrationKey(ig: any): string {
137
- return ig.aliasName || ig.srcName || ig.id;
138
- }
139
-
140
- async function fetchWorkspaceIntegrations(baseUrl: string, workspaceId: string, headers: any): Promise<any[]> {
141
- try {
142
- const wkRes = await axios.get(`${baseUrl}/api/workspace/${workspaceId}`, { headers });
143
- const wsData = Array.isArray(wkRes.data) ? wkRes.data[0] : wkRes.data;
144
- return wsData?.integrations || [];
145
- } catch {
146
- return [];
147
- }
148
- }
149
-
150
- async function activateIntegration(baseUrl: string, workspaceId: string, integrationId: string, headers: any): Promise<boolean> {
151
- try {
152
- const res = await axios.post(
153
- `${baseUrl}/api/workspace/${workspaceId}/integrations/${integrationId}/activate`,
154
- { isActive: true },
155
- { headers }
156
- );
157
- return res.data?.success === true;
158
- } catch {
159
- return false;
160
- }
161
- }
162
-
163
- export async function connectChannelFlow(ctx: WizardContext): Promise<AgentChannel[]> {
164
- const channels: AgentChannel[] = [];
165
- const baseUrl = getBaseUrl(ctx.zone, ctx.dev);
166
- const headers = {
167
- 'Authorization': `Bearer ${ctx.apiKey}`,
168
- 'x-workspace-id': ctx.workspaceId,
169
- 'Content-Type': 'application/json',
170
- };
171
-
172
- let addMore = true;
173
- while (addMore) {
174
- const { channelType } = await (inquirer as any).prompt([{
175
- type: 'list',
176
- name: 'channelType',
177
- message: 'Which channel do you want to connect?',
178
- choices: CHANNEL_CHOICES,
179
- }]);
180
-
181
- const channelLabel = CHANNEL_CHOICES.find(c => c.value === channelType)?.name || channelType;
182
- const targetType = channelType === 'whatsapp_business' ? 'whatsapp' : channelType;
183
-
184
- // Cargar integraciones existentes del tipo seleccionado
185
- const loadSpinner = ora({ text: chalk.gray('Loading integrations...'), spinner: 'dots', color: 'green' }).start();
186
- const allIntegrations = await fetchWorkspaceIntegrations(baseUrl, ctx.workspaceId, headers);
187
- const existing = allIntegrations.filter((ig: any) => ig.type === targetType);
188
- loadSpinner.stop();
189
-
190
- let selectedIntegration: any = null;
191
-
192
- if (existing.length > 0) {
193
- // Mostrar integraciones existentes + opcion de agregar nueva
194
- const choices = existing.map((ig: any) => ({
195
- name: `${getIntegrationLabel(ig)}`,
196
- value: ig.id,
197
- }));
198
- choices.push(new (inquirer as any).Separator());
199
- choices.push({ name: chalk.hex('#4ade80')('+ Add a new one (generate link)'), value: '__new__' });
200
-
201
- const { selected } = await (inquirer as any).prompt([{
202
- type: 'list',
203
- name: 'selected',
204
- message: `Available ${channelLabel} integrations:`,
205
- choices,
206
- }]);
207
-
208
- if (selected !== '__new__') {
209
- // Selecciono una existente
210
- selectedIntegration = existing.find((ig: any) => ig.id === selected);
211
- const label = getIntegrationLabel(selectedIntegration);
212
-
213
- // Activar la integracion si no esta activa
214
- if (!selectedIntegration.isActive) {
215
- const actSpinner = ora({ text: chalk.gray('Activating integration...'), spinner: 'dots', color: 'green' }).start();
216
- const activated = await activateIntegration(baseUrl, ctx.workspaceId, selectedIntegration.id, headers);
217
- if (activated) {
218
- actSpinner.succeed(chalk.hex('#4ade80')('Integration activated successfully.'));
219
- } else {
220
- actSpinner.warn(chalk.hex('#FFA726')('Could not activate the integration. You can activate it manually from the platform.'));
221
- }
222
- }
223
-
224
- channels.push({
225
- channel: CHANNEL_AGENT_MAP[channelType],
226
- key: getIntegrationKey(selectedIntegration),
227
- multianswer: false,
228
- });
229
- console.log(chalk.hex('#4ade80')(` Channel linked: ${label}`));
230
- }
231
- }
232
-
233
- // Si no hay existentes o eligio "Agregar nuevo" → generar link de onboarding
234
- if (!selectedIntegration) {
235
- const spinner = ora({ text: chalk.gray('Generating connection link...'), spinner: 'dots', color: 'green' }).start();
236
-
237
- try {
238
- const linkRes = await axios.post(
239
- `${baseUrl}/api/workspace/${ctx.workspaceId}/onboarding-link`,
240
- { type: channelType },
241
- { headers }
242
- );
243
-
244
- if (!linkRes.data?.success) {
245
- spinner.fail(chalk.hex('#EF5350')(`Error: ${linkRes.data?.message || 'Could not generate the link'}`));
246
- } else {
247
- const shortUrl = linkRes.data.data?.shortUrl || '';
248
- spinner.stop();
249
-
250
- // Mostrar link
251
- console.log();
252
- console.log(chalk.hex('#4CAF50')(' ─'.repeat(32)));
253
- console.log(chalk.bold(` Send this link to connect ${channelLabel}:`));
254
- console.log();
255
- console.log(' ' + chalk.hex('#22d3ee')(shortUrl));
256
- console.log();
257
- console.log(chalk.hex('#4CAF50')(' ─'.repeat(32)));
258
- console.log();
259
-
260
- // Guardar IDs actuales para detectar nuevos
261
- const prevIds = new Set(allIntegrations.map((i: any) => i.id));
262
-
263
- // Polling para detectar nueva integracion (Enter para saltar)
264
- const pollSpinner = ora({ text: chalk.gray('Waiting for connection... (Enter to skip)'), spinner: 'dots', color: 'cyan' }).start();
265
- const POLL_INTERVAL = 5000;
266
- const MAX_POLLS = 60;
267
- let detected: any = null;
268
- let skipped = false;
269
-
270
- // Escuchar Enter para saltar
271
- const skipPromise = new Promise<void>(resolve => {
272
- const onData = (data: Buffer) => {
273
- if (data.toString().includes('\n') || data.toString().includes('\r')) {
274
- skipped = true;
275
- process.stdin.removeListener('data', onData);
276
- if (wasRaw) process.stdin.setRawMode(false);
277
- resolve();
278
- }
279
- };
280
- const wasRaw = process.stdin.isRaw;
281
- if (process.stdin.isTTY) process.stdin.setRawMode(true);
282
- process.stdin.resume();
283
- process.stdin.on('data', onData);
284
- setTimeout(() => {
285
- process.stdin.removeListener('data', onData);
286
- if (process.stdin.isTTY && wasRaw !== undefined) {
287
- try { process.stdin.setRawMode(wasRaw); } catch {}
288
- }
289
- resolve();
290
- }, POLL_INTERVAL * MAX_POLLS + 1000);
291
- });
292
-
293
- for (let i = 0; i < MAX_POLLS; i++) {
294
- await Promise.race([
295
- new Promise(r => setTimeout(r, POLL_INTERVAL)),
296
- skipPromise,
297
- ]);
298
- if (skipped) break;
299
- try {
300
- const freshIntegrations = await fetchWorkspaceIntegrations(baseUrl, ctx.workspaceId, headers);
301
- const newOnes = freshIntegrations.filter((ig: any) => !prevIds.has(ig.id));
302
- if (newOnes.length > 0) {
303
- const match = newOnes.find((ig: any) => ig.type === targetType);
304
- detected = match || newOnes[0];
305
- break;
306
- }
307
- } catch {}
308
- const elapsed = Math.floor(((i + 1) * POLL_INTERVAL) / 1000);
309
- pollSpinner.text = chalk.gray(`Waiting for connection... ${elapsed}s (Enter to skip)`);
310
- }
311
-
312
- if (detected) {
313
- const detectedName = getIntegrationLabel(detected);
314
- pollSpinner.succeed(chalk.hex('#4ade80')(`Connection detected: ${detectedName}`));
315
-
316
- // Activar la integracion detectada
317
- const actSpinner = ora({ text: chalk.gray('Activating integration...'), spinner: 'dots', color: 'green' }).start();
318
- const activated = await activateIntegration(baseUrl, ctx.workspaceId, detected.id, headers);
319
- if (activated) {
320
- actSpinner.succeed(chalk.hex('#4ade80')('Integration activated successfully.'));
321
- } else {
322
- actSpinner.warn(chalk.hex('#FFA726')('Could not activate the integration. You can activate it manually from the platform.'));
323
- }
324
-
325
- channels.push({
326
- channel: CHANNEL_AGENT_MAP[channelType],
327
- key: getIntegrationKey(detected),
328
- multianswer: false,
329
- });
330
- console.log(chalk.hex('#4ade80')(' Channel linked successfully.'));
331
- } else {
332
- pollSpinner.warn(chalk.hex('#FFA726')(skipped ? 'Skipped.' : 'Timeout: no connection detected.'));
333
- const { action } = await (inquirer as any).prompt([{
334
- type: 'list', name: 'action',
335
- message: 'What would you like to do?',
336
- choices: [
337
- { name: 'Enter ID manually', value: 'manual' },
338
- { name: 'Skip', value: 'skip' },
339
- ],
340
- }]);
341
- if (action === 'manual') {
342
- const { key } = await (inquirer as any).prompt([
343
- { type: 'input', name: 'key', message: `${channelLabel} channel ID/number:`, validate: (v: string) => v.length > 0 || 'Required' },
344
- ]);
345
-
346
- // Activar la integracion ingresada manualmente
347
- const actSpinner = ora({ text: chalk.gray('Activating integration...'), spinner: 'dots', color: 'green' }).start();
348
- const activated = await activateIntegration(baseUrl, ctx.workspaceId, key, headers);
349
- if (activated) {
350
- actSpinner.succeed(chalk.hex('#4ade80')('Integration activated successfully.'));
351
- } else {
352
- actSpinner.warn(chalk.hex('#FFA726')('Could not activate the integration. You can activate it manually from the platform.'));
353
- }
354
-
355
- channels.push({ channel: CHANNEL_AGENT_MAP[channelType], key, multianswer: false });
356
- }
357
- }
358
- }
359
- } catch (err) {
360
- spinner.fail(chalk.hex('#EF5350')(`Error: ${err instanceof Error ? err.message : err}`));
361
- }
362
- }
363
-
364
- const { more } = await (inquirer as any).prompt([{ type: 'confirm', name: 'more', message: 'Connect another channel?', default: false }]);
365
- addMore = more;
366
- }
367
-
368
- return channels;
369
- }
370
-
371
- export async function runAgentWizard(zone: string, workspaceId?: string, apiKey?: string, dev?: boolean): Promise<AgentConfig> {
372
- console.log(section('Create new AI agent'));
373
- console.log(theme.muted(' Answer the following questions to configure your agent.\n'));
374
-
375
- // Paso 1: Informacion basica
376
- console.log(theme.bold('\n Step 1/8: Basic information'));
377
- const basic = await (inquirer as any).prompt([
378
- {
379
- type: 'input',
380
- name: 'name',
381
- message: 'Agent name:',
382
- validate: (v: string) => v.length > 0 || 'Name is required',
383
- },
384
- {
385
- type: 'input',
386
- name: 'description',
387
- message: 'Short description:',
388
- default: '',
389
- },
390
- {
391
- type: 'input',
392
- name: 'prompt',
393
- message: 'System prompt (main instructions):',
394
- default: 'You are a friendly and professional virtual assistant.',
395
- },
396
- {
397
- type: 'number',
398
- name: 'buffer',
399
- message: 'Conversation buffer (3-20 messages):',
400
- default: 5,
401
- validate: (v: number) => (v >= 3 && v <= 20) || 'Must be between 3 and 20',
402
- },
403
- {
404
- type: 'list',
405
- name: 'color',
406
- message: 'Agent color:',
407
- choices: COLORS,
408
- default: 'blue',
409
- },
410
- ]);
411
-
412
- // Paso 2: Personalidad
413
- console.log(theme.bold('\n Step 2/8: Personality and instructions'));
414
- const personality = await (inquirer as any).prompt([
415
- {
416
- type: 'list',
417
- name: 'tone',
418
- message: 'Communication tone:',
419
- choices: ['profesional', 'amigable', 'formal', 'casual', 'tecnico', 'empatico'],
420
- default: 'profesional',
421
- },
422
- {
423
- type: 'list',
424
- name: 'style',
425
- message: 'Response style:',
426
- choices: ['conciso', 'detallado', 'conversacional', 'directo'],
427
- default: 'conciso',
428
- },
429
- {
430
- type: 'input',
431
- name: 'personality',
432
- message: 'Personality in one sentence:',
433
- default: 'Helpful and knowledgeable',
434
- },
435
- {
436
- type: 'input',
437
- name: 'objective',
438
- message: 'Main objective of the agent:',
439
- default: 'Help users with their queries',
440
- },
441
- {
442
- type: 'list',
443
- name: 'language',
444
- message: 'Primary language:',
445
- choices: ['Espanol', 'English', 'Portugues', 'Frances'],
446
- default: 'Espanol',
447
- },
448
- {
449
- type: 'confirm',
450
- name: 'useEmojis',
451
- message: 'Use emojis in responses?',
452
- default: true,
453
- },
454
- ]);
455
-
456
- // Paso 3: Persona
457
- console.log(theme.bold('\n Step 3/8: Agent identity'));
458
- const person = await (inquirer as any).prompt([
459
- {
460
- type: 'input',
461
- name: 'name',
462
- message: 'Character name (how it introduces itself):',
463
- default: basic.name,
464
- },
465
- {
466
- type: 'input',
467
- name: 'role',
468
- message: 'Role (e.g. Sales assistant, Tech support):',
469
- default: 'Virtual assistant',
470
- },
471
- {
472
- type: 'confirm',
473
- name: 'firstPerson',
474
- message: 'Speak in first person?',
475
- default: true,
476
- },
477
- ]);
478
-
479
- // Paso 4: Fallbacks
480
- console.log(theme.bold('\n Step 4/8: Fallback messages'));
481
- const fallbacks = await (inquirer as any).prompt([
482
- {
483
- type: 'input',
484
- name: 'noAnswer',
485
- message: 'When it has no answer:',
486
- default: 'Lo siento, no tengo informacion sobre eso. Puedo ayudarte con otra consulta?',
487
- },
488
- {
489
- type: 'input',
490
- name: 'serviceError',
491
- message: 'When there is a service error:',
492
- default: 'Disculpa, estamos experimentando dificultades tecnicas. Intenta de nuevo en unos momentos.',
493
- },
494
- {
495
- type: 'input',
496
- name: 'misunderstanding',
497
- message: 'When it does not understand the question:',
498
- default: 'No estoy seguro de entender tu consulta. Podrias reformularla?',
499
- },
500
- ]);
501
-
502
- // Paso 5: Tool Calling
503
- console.log(theme.bold('\n Step 5/8: Tool Calling'));
504
- console.log(theme.muted(' Tool Calling lets the agent execute automatic actions\n'));
505
- const { useToolCalling } = await (inquirer as any).prompt([
506
- {
507
- type: 'confirm',
508
- name: 'useToolCalling',
509
- message: 'Enable Tool Calling?',
510
- default: true,
511
- },
512
- ]);
513
-
514
- // Servicios (API calls)
515
- const services: AgentService[] = [];
516
- if (useToolCalling) {
517
- const { addServices } = await (inquirer as any).prompt([{
518
- type: 'confirm',
519
- name: 'addServices',
520
- message: 'Add external services (API calls)?',
521
- default: false,
522
- }]);
523
-
524
- if (addServices) {
525
- let addMore = true;
526
- while (addMore) {
527
- console.log(theme.muted('\n New service:'));
528
- const svc = await (inquirer as any).prompt([
529
- { type: 'input', name: 'intent', message: 'Service name/intent:', validate: (v: string) => v.length > 0 || 'Required' },
530
- { type: 'input', name: 'reference', message: 'Reference keywords:', default: '' },
531
- { type: 'list', name: 'method', message: 'HTTP method:', choices: ['GET', 'POST'] },
532
- { type: 'input', name: 'endpoint', message: 'Endpoint URL:', validate: (v: string) => v.length > 0 || 'Required' },
533
- { type: 'input', name: 'responseMessage', message: 'Response message:', default: '' },
534
- ]);
535
-
536
- // Required fields
537
- const fields: AgentService['requiredFields'] = [];
538
- const { addFields } = await (inquirer as any).prompt([{
539
- type: 'confirm', name: 'addFields',
540
- message: 'Add required fields?', default: false,
541
- }]);
542
- if (addFields) {
543
- let moreFields = true;
544
- while (moreFields) {
545
- const field = await (inquirer as any).prompt([
546
- { type: 'input', name: 'name', message: 'Field name:' },
547
- { type: 'list', name: 'type', message: 'Type:', choices: ['string', 'number', 'boolean', 'date'] },
548
- { type: 'input', name: 'description', message: 'Description:' },
549
- { type: 'input', name: 'promptHint', message: 'Hint for the LLM:' },
550
- ]);
551
- fields.push(field);
552
- const { more } = await (inquirer as any).prompt([{ type: 'confirm', name: 'more', message: 'Add another field?', default: false }]);
553
- moreFields = more;
554
- }
555
- }
556
-
557
- services.push({
558
- intent: svc.intent,
559
- reference: svc.reference,
560
- enabled: true,
561
- method: svc.method,
562
- endpoint: svc.endpoint,
563
- requiredFields: fields,
564
- headers: {},
565
- bodyTemplate: {},
566
- responseMapping: {},
567
- responseMessage: svc.responseMessage,
568
- responseConditions: [],
569
- });
570
-
571
- const { more } = await (inquirer as any).prompt([{ type: 'confirm', name: 'more', message: 'Add another service?', default: false }]);
572
- addMore = more;
573
- }
574
- }
575
- }
576
-
577
- // Acciones
578
- const actions: AgentAction[] = [];
579
- if (useToolCalling) {
580
- const { addActions } = await (inquirer as any).prompt([{
581
- type: 'confirm',
582
- name: 'addActions',
583
- message: 'Add actions (tag, stage, assign agent)?',
584
- default: false,
585
- }]);
586
-
587
- if (addActions) {
588
- let addMore = true;
589
- while (addMore) {
590
- console.log(theme.muted('\n New action:'));
591
- const act = await (inquirer as any).prompt([
592
- { type: 'input', name: 'intent', message: 'Action name/intent:', validate: (v: string) => v.length > 0 || 'Required' },
593
- { type: 'input', name: 'reference', message: 'Reference keywords:', default: '' },
594
- {
595
- type: 'list', name: 'actionType', message: 'Action type:',
596
- choices: [
597
- { name: 'Schedule event', value: 'action.event.add' },
598
- { name: 'Update event (reschedule)', value: 'action.event.update' },
599
- { name: 'List events', value: 'action.event.list' },
600
- { name: 'Delete event (cancel)', value: 'action.event.delete' },
601
- { name: 'Add tag', value: 'action.tag' },
602
- { name: 'Change stage', value: 'action.stage' },
603
- { name: 'Hand off to human agent', value: 'action.agentShutDown' },
604
- { name: 'Mark as resolved', value: 'action.solved' },
605
- { name: 'Assign agent', value: 'action.asign' },
606
- { name: 'Segmentation', value: 'action.segmentation' },
607
- ],
608
- },
609
- { type: 'input', name: 'actionValue', message: 'Action value:', default: '' },
610
- { type: 'input', name: 'responseMessage', message: 'Response message:', default: '' },
611
- ]);
612
-
613
- actions.push({
614
- intent: act.intent,
615
- reference: act.reference,
616
- enabled: true,
617
- requiredFields: [],
618
- responseMessage: act.responseMessage,
619
- action: [{ type: act.actionType, value: act.actionValue }],
620
- });
621
-
622
- const { more } = await (inquirer as any).prompt([{ type: 'confirm', name: 'more', message: 'Add another action?', default: false }]);
623
- addMore = more;
624
- }
625
- }
626
- }
627
-
628
- // Paso 6: AI Provider
629
- console.log(theme.bold('\n Step 6/8: AI provider'));
630
- const { configureAI } = await (inquirer as any).prompt([{
631
- type: 'confirm',
632
- name: 'configureAI',
633
- message: 'Configure a custom AI provider?',
634
- default: false,
635
- }]);
636
-
637
- const aiProviders: AIProvider[] = [];
638
- if (configureAI) {
639
- const ai = await (inquirer as any).prompt([
640
- { type: 'list', name: 'provider', message: 'Provider:', choices: ['openai', 'claude', 'gemini'] },
641
- ]);
642
- const models = MODELS[ai.provider] || MODELS.openai;
643
- const aiConfig = await (inquirer as any).prompt([
644
- { type: 'list', name: 'model', message: 'Model:', choices: models },
645
- { type: 'password', name: 'apiToken', message: 'API Token:', mask: '*', validate: (v: string) => v.length > 0 || 'Required' },
646
- ]);
647
-
648
- aiProviders.push({
649
- provider: ai.provider,
650
- model: aiConfig.model,
651
- apiToken: aiConfig.apiToken,
652
- temperature: 0.7,
653
- maxTokens: 4096,
654
- isDefault: true,
655
- });
656
- }
657
-
658
- // Paso 7: Conectar canal
659
- console.log(theme.bold('\n Step 7/8: Connect channel'));
660
- let channels: AgentChannel[] = [];
661
- const { addChannel } = await (inquirer as any).prompt([{
662
- type: 'confirm',
663
- name: 'addChannel',
664
- message: 'Connect a channel (WhatsApp, Instagram, Messenger)?',
665
- default: false,
666
- }]);
667
-
668
- if (addChannel && workspaceId && apiKey) {
669
- channels = await connectChannelFlow({ zone, workspaceId, apiKey, dev: dev || false });
670
- } else if (addChannel) {
671
- // Fallback manual si no hay credenciales para API
672
- const wa = await (inquirer as any).prompt([
673
- { type: 'input', name: 'key', message: 'WhatsApp number (with country code):', validate: (v: string) => v.length > 0 || 'Required' },
674
- { type: 'confirm', name: 'multianswer', message: 'Allow multiple answers?', default: false },
675
- ]);
676
- channels.push({ channel: 'whatsapp', key: wa.key, multianswer: wa.multianswer });
677
- }
678
-
679
- // Paso 8: Ejemplos
680
- console.log(theme.bold('\n Step 8/8: Conversation examples'));
681
- const examples: AgentExample[] = [];
682
- const { addExamples } = await (inquirer as any).prompt([{
683
- type: 'confirm',
684
- name: 'addExamples',
685
- message: 'Add conversation examples?',
686
- default: false,
687
- }]);
688
-
689
- if (addExamples) {
690
- let addMore = true;
691
- while (addMore) {
692
- const ex = await (inquirer as any).prompt([
693
- { type: 'input', name: 'user', message: 'User message:' },
694
- { type: 'input', name: 'agent', message: 'Agent response:' },
695
- ]);
696
- examples.push({ value: `User: ${ex.user}\nAgent: ${ex.agent}`, color: 'blue' });
697
- const { more } = await (inquirer as any).prompt([{ type: 'confirm', name: 'more', message: 'Add another example?', default: false }]);
698
- addMore = more;
699
- }
700
- }
701
-
702
- // Armar config final (alineado con agent.config.schema.json del backend)
703
- const config: AgentConfig = {
704
- name: basic.name,
705
- description: basic.description || '',
706
- prompt: basic.prompt,
707
- buffer: basic.buffer,
708
- zone,
709
- color: basic.color,
710
- useToolCalling,
711
- customAIConfig: configureAI,
712
- aiProviders,
713
- instructions: {
714
- tone: personality.tone,
715
- style: personality.style,
716
- personality: personality.personality,
717
- objective: personality.objective,
718
- language: personality.language,
719
- emojis: personality.useEmojis,
720
- },
721
- person: {
722
- name: person.name,
723
- role: person.role,
724
- speaksInFirstPerson: person.firstPerson,
725
- },
726
- fallbacks: {
727
- noAnswer: fallbacks.noAnswer,
728
- serviceError: fallbacks.serviceError,
729
- doNotUnderstand: fallbacks.misunderstanding,
730
- },
731
- services,
732
- actions,
733
- channels,
734
- examples,
735
- tags: [],
736
- };
737
-
738
- // Preview
739
- console.log(section('Configuration preview'));
740
- console.log(kvPair('Name', config.name));
741
- console.log(kvPair('Prompt', config.prompt.substring(0, 80) + '...'));
742
- console.log(kvPair('Tool Calling', config.useToolCalling ? 'Enabled' : 'Disabled'));
743
- console.log(kvPair('Services', String(config.services.length)));
744
- console.log(kvPair('Actions', String(config.actions.length)));
745
- console.log(kvPair('AI Provider', config.customAIConfig ? config.aiProviders[0]?.provider + ' / ' + config.aiProviders[0]?.model : 'Default (Plazbot)'));
746
- if (config.channels.length > 0) {
747
- config.channels.forEach((ch, i) => {
748
- console.log(kvPair(`Channel ${i + 1} (${ch.channel})`, ch.key));
749
- });
750
- } else {
751
- console.log(kvPair('Channels', 'Not configured'));
752
- }
753
- console.log();
754
-
755
- return config;
756
- }