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.
- package/README.md +38 -2
- package/dist/commands/auth/browser-login.js +121 -0
- package/dist/commands/auth/index.js +5 -1
- package/dist/commands/auth/login.js +1 -3
- package/dist/commands/auth/logout.js +4 -3
- package/dist/commands/auth/status.js +8 -5
- package/dist/commands/auth/workspace.js +73 -0
- package/dist/commands/whatsapp/connect-flow.js +137 -0
- package/dist/commands/whatsapp/connect.js +26 -160
- package/dist/commands/whatsapp/index.js +4 -0
- package/dist/commands/whatsapp/setup.js +242 -0
- package/dist/schemas/agent.config.schema.json +24 -0
- package/dist/studio/components/App.js +1 -1
- package/dist/studio/runOneShot.js +2 -2
- package/dist/studio/runRepl.js +1 -1
- package/dist/utils/agent-errors.js +2 -2
- package/dist/utils/banner.js +10 -9
- package/dist/utils/credentials.js +31 -3
- package/dist/utils/open-browser.js +17 -0
- package/package.json +14 -6
- package/.claude/settings.local.json +0 -19
- package/CLAUDE.md +0 -121
- package/agent.config.json +0 -21
- package/dist/studio/components/AgentPanel.js +0 -35
- package/dist/studio/render/json.js +0 -16
- package/dist/studio/whatsapp/api.js +0 -63
- package/dist/studio/whatsapp/polling.js +0 -77
- package/dist/studio/whatsapp/types.js +0 -31
- package/src/cli.ts +0 -57
- package/src/commands/agent/ai-config.ts +0 -164
- package/src/commands/agent/chat.ts +0 -225
- package/src/commands/agent/copy.ts +0 -46
- package/src/commands/agent/create.ts +0 -103
- package/src/commands/agent/delete.ts +0 -71
- package/src/commands/agent/enable-widget.ts +0 -67
- package/src/commands/agent/export.ts +0 -430
- package/src/commands/agent/files.ts +0 -171
- package/src/commands/agent/get.ts +0 -233
- package/src/commands/agent/index.ts +0 -55
- package/src/commands/agent/list.ts +0 -58
- package/src/commands/agent/monitor.ts +0 -375
- package/src/commands/agent/on-message.ts +0 -82
- package/src/commands/agent/set.ts +0 -149
- package/src/commands/agent/templates.ts +0 -892
- package/src/commands/agent/tools.ts +0 -168
- package/src/commands/agent/update.ts +0 -61
- package/src/commands/agent/validate.ts +0 -135
- package/src/commands/agent/wizard.ts +0 -756
- package/src/commands/auth/index.ts +0 -10
- package/src/commands/auth/login.ts +0 -72
- package/src/commands/auth/logout.ts +0 -28
- package/src/commands/auth/status.ts +0 -28
- package/src/commands/portal/add-agent.ts +0 -66
- package/src/commands/portal/add-link.ts +0 -47
- package/src/commands/portal/clear-links.ts +0 -33
- package/src/commands/portal/create.ts +0 -58
- package/src/commands/portal/delete.ts +0 -70
- package/src/commands/portal/get.ts +0 -77
- package/src/commands/portal/index.ts +0 -32
- package/src/commands/portal/list.ts +0 -79
- package/src/commands/portal/update.ts +0 -93
- package/src/commands/whatsapp/broadcast.ts +0 -110
- package/src/commands/whatsapp/channels.ts +0 -109
- package/src/commands/whatsapp/chat.ts +0 -85
- package/src/commands/whatsapp/connect.ts +0 -220
- package/src/commands/whatsapp/delete-webhook.ts +0 -43
- package/src/commands/whatsapp/index.ts +0 -37
- package/src/commands/whatsapp/register-webhook.ts +0 -47
- package/src/commands/whatsapp/send-template.ts +0 -94
- package/src/commands/whatsapp/send.ts +0 -55
- package/src/commands/whatsapp/widget.ts +0 -75
- package/src/commands/workers/deploy.ts +0 -156
- package/src/commands/workers/index.ts +0 -30
- package/src/commands/workers/list.ts +0 -105
- package/src/commands/workers/logs.ts +0 -99
- package/src/commands/workers/remove.ts +0 -103
- package/src/commands/workers/secret.ts +0 -191
- package/src/commands/workers/test.ts +0 -145
- package/src/schemas/agent.config.schema.json +0 -569
- package/src/studio/api/sseClient.ts +0 -91
- package/src/studio/api/studioApi.ts +0 -27
- package/src/studio/api/types.ts +0 -96
- package/src/studio/components/App.tsx +0 -274
- package/src/studio/components/ChatLog.tsx +0 -121
- package/src/studio/components/ConnectCard.tsx +0 -171
- package/src/studio/components/Footer.tsx +0 -38
- package/src/studio/components/Header.tsx +0 -39
- package/src/studio/components/Input.tsx +0 -176
- package/src/studio/components/Message.tsx +0 -87
- package/src/studio/components/Suggestions.tsx +0 -26
- package/src/studio/components/ToolCall.tsx +0 -58
- package/src/studio/connect/api.ts +0 -98
- package/src/studio/connect/polling.ts +0 -94
- package/src/studio/connect/types.ts +0 -141
- package/src/studio/index.ts +0 -58
- package/src/studio/render/markdown.ts +0 -93
- package/src/studio/render/steps.ts +0 -57
- package/src/studio/runOneShot.ts +0 -114
- package/src/studio/runRepl.tsx +0 -82
- package/src/studio/slash/handlers.ts +0 -226
- package/src/studio/slash/parser.ts +0 -41
- package/src/studio/slash/registry.ts +0 -54
- package/src/studio/state/store.ts +0 -279
- package/src/types/agent.ts +0 -63
- package/src/types/auth.ts +0 -9
- package/src/types/common.ts +0 -10
- package/src/types/message.ts +0 -34
- package/src/types/portal.ts +0 -56
- package/src/types/workers.ts +0 -86
- package/src/utils/agent-errors.ts +0 -67
- package/src/utils/api.ts +0 -35
- package/src/utils/banner.ts +0 -99
- package/src/utils/credentials.ts +0 -38
- package/src/utils/help.ts +0 -51
- package/src/utils/logger.ts +0 -40
- package/src/utils/ui.ts +0 -109
- 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
|
-
}
|