plazbot-cli 0.2.23 → 0.2.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.whatsappConnectCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const axios_1 = __importDefault(require("axios"));
9
+ const ora_1 = __importDefault(require("ora"));
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ const credentials_1 = require("../../utils/credentials");
12
+ const logger_1 = require("../../utils/logger");
13
+ const ui_1 = require("../../utils/ui");
14
+ function getBaseUrl(zone, dev) {
15
+ if (dev)
16
+ return 'http://localhost:5090';
17
+ return zone === 'EU' ? 'https://apieu.plazbot.com' : 'https://api.plazbot.com';
18
+ }
19
+ function getIntegrationLabel(ig) {
20
+ if (ig.type === 'whatsapp') {
21
+ return ig.aliasName
22
+ ? `${ig.aliasName} (${ig.cellphoneNumberFormat || ig.cellphoneNumber || ''})`
23
+ : ig.cellphoneNumberFormat || ig.cellphoneNumber || ig.id;
24
+ }
25
+ return ig.aliasName || ig.srcName || ig.id;
26
+ }
27
+ async function fetchWorkspaceIntegrations(baseUrl, workspaceId, headers) {
28
+ try {
29
+ const wkRes = await axios_1.default.get(`${baseUrl}/api/workspace/${workspaceId}`, { headers });
30
+ const wsData = Array.isArray(wkRes.data) ? wkRes.data[0] : wkRes.data;
31
+ return wsData?.integrations || [];
32
+ }
33
+ catch {
34
+ return [];
35
+ }
36
+ }
37
+ async function activateIntegration(baseUrl, workspaceId, integrationId, headers) {
38
+ try {
39
+ const res = await axios_1.default.post(`${baseUrl}/api/workspace/${workspaceId}/integrations/${integrationId}/activate`, { isActive: true }, { headers });
40
+ return res.data?.success === true;
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
46
+ exports.whatsappConnectCommand = new commander_1.Command('connect')
47
+ .description('Conectar un numero de WhatsApp al workspace (sin crear agente)')
48
+ .option('--dev', 'Usar ambiente de desarrollo', false)
49
+ .option('--business', 'Conectar como WhatsApp Business', false)
50
+ .action(async (options) => {
51
+ try {
52
+ const credentials = await (0, credentials_1.getStoredCredentials)();
53
+ const baseUrl = getBaseUrl(credentials.zone, options.dev);
54
+ const headers = {
55
+ 'Authorization': `Bearer ${credentials.apiKey}`,
56
+ 'x-workspace-id': credentials.workspace,
57
+ 'Content-Type': 'application/json',
58
+ };
59
+ const channelType = options.business ? 'whatsapp_business' : 'whatsapp';
60
+ const channelLabel = options.business ? 'WhatsApp Business' : 'WhatsApp';
61
+ console.log((0, ui_1.section)(`Conectar ${channelLabel}`));
62
+ console.log();
63
+ // Cargar integraciones existentes
64
+ const loadSpinner = (0, ora_1.default)({ text: chalk_1.default.gray('Cargando integraciones existentes...'), spinner: 'dots', color: 'green' }).start();
65
+ const allIntegrations = await fetchWorkspaceIntegrations(baseUrl, credentials.workspace, headers);
66
+ const existing = allIntegrations.filter((ig) => ig.type === 'whatsapp');
67
+ loadSpinner.stop();
68
+ if (existing.length > 0) {
69
+ console.log(ui_1.theme.muted(` ${existing.length} numero(s) ya conectado(s):`));
70
+ existing.forEach((ig) => {
71
+ const label = getIntegrationLabel(ig);
72
+ const status = ig.isActive ? chalk_1.default.hex('#66BB6A')('activo') : chalk_1.default.hex('#EF5350')('inactivo');
73
+ console.log(` ${ui_1.theme.muted('•')} ${label} ${status}`);
74
+ });
75
+ console.log();
76
+ }
77
+ // Generar link de onboarding
78
+ const spinner = (0, ora_1.default)({ text: chalk_1.default.gray('Generando link de conexion...'), spinner: 'dots', color: 'green' }).start();
79
+ const linkRes = await axios_1.default.post(`${baseUrl}/api/workspace/${credentials.workspace}/onboarding-link`, { type: channelType }, { headers });
80
+ if (!linkRes.data?.success) {
81
+ spinner.fail(chalk_1.default.hex('#EF5350')(`Error: ${linkRes.data?.message || 'No se pudo generar el link'}`));
82
+ process.exit(1);
83
+ }
84
+ const shortUrl = linkRes.data.data?.shortUrl || '';
85
+ spinner.stop();
86
+ // Mostrar link
87
+ console.log(chalk_1.default.hex('#25D366')(' ┌' + '─'.repeat(54) + '┐'));
88
+ console.log(chalk_1.default.hex('#25D366')(' │') + chalk_1.default.bold(` Abre este link en tu navegador:`).padEnd(64) + chalk_1.default.hex('#25D366')('│'));
89
+ console.log(chalk_1.default.hex('#25D366')(' │') + ''.padEnd(54) + chalk_1.default.hex('#25D366')('│'));
90
+ console.log(chalk_1.default.hex('#25D366')(' │') + chalk_1.default.hex('#22d3ee')(` ${shortUrl}`).padEnd(64) + chalk_1.default.hex('#25D366')('│'));
91
+ console.log(chalk_1.default.hex('#25D366')(' │') + ''.padEnd(54) + chalk_1.default.hex('#25D366')('│'));
92
+ console.log(chalk_1.default.hex('#25D366')(' │') + chalk_1.default.gray(' Escanea el codigo QR desde tu WhatsApp').padEnd(64) + chalk_1.default.hex('#25D366')('│'));
93
+ console.log(chalk_1.default.hex('#25D366')(' └' + '─'.repeat(54) + '┘'));
94
+ console.log();
95
+ // Guardar IDs actuales para detectar nuevos
96
+ const prevIds = new Set(allIntegrations.map((i) => i.id));
97
+ // Polling para detectar nueva integracion
98
+ const pollSpinner = (0, ora_1.default)({ text: chalk_1.default.gray('Esperando conexion... (Ctrl+C para cancelar)'), spinner: 'dots', color: 'cyan' }).start();
99
+ const POLL_INTERVAL = 5000;
100
+ const MAX_POLLS = 60; // 5 minutos
101
+ let detected = null;
102
+ for (let i = 0; i < MAX_POLLS; i++) {
103
+ await new Promise(r => setTimeout(r, POLL_INTERVAL));
104
+ try {
105
+ const freshIntegrations = await fetchWorkspaceIntegrations(baseUrl, credentials.workspace, headers);
106
+ const newOnes = freshIntegrations.filter((ig) => !prevIds.has(ig.id));
107
+ if (newOnes.length > 0) {
108
+ const match = newOnes.find((ig) => ig.type === 'whatsapp');
109
+ detected = match || newOnes[0];
110
+ break;
111
+ }
112
+ }
113
+ catch { }
114
+ const elapsed = Math.floor(((i + 1) * POLL_INTERVAL) / 1000);
115
+ pollSpinner.text = chalk_1.default.gray(`Esperando conexion... ${elapsed}s (Ctrl+C para cancelar)`);
116
+ }
117
+ if (detected) {
118
+ const detectedName = getIntegrationLabel(detected);
119
+ pollSpinner.succeed(chalk_1.default.hex('#4ade80')(`Conexion detectada: ${detectedName}`));
120
+ // Activar la integracion
121
+ const actSpinner = (0, ora_1.default)({ text: chalk_1.default.gray('Activando integracion...'), spinner: 'dots', color: 'green' }).start();
122
+ const activated = await activateIntegration(baseUrl, credentials.workspace, detected.id, headers);
123
+ if (activated) {
124
+ actSpinner.succeed(chalk_1.default.hex('#4ade80')('Integracion activada correctamente'));
125
+ }
126
+ else {
127
+ actSpinner.warn(chalk_1.default.hex('#FFA726')('No se pudo activar automaticamente. Activala desde la plataforma.'));
128
+ }
129
+ console.log();
130
+ console.log(ui_1.theme.success(' WhatsApp conectado exitosamente'));
131
+ console.log(ui_1.theme.muted(` Numero: ${detectedName}`));
132
+ console.log();
133
+ console.log(ui_1.theme.muted(' Ahora puedes:'));
134
+ console.log(ui_1.theme.muted(' plazbot whatsapp send-message Enviar un mensaje'));
135
+ console.log(ui_1.theme.muted(' plazbot whatsapp chat <phone> Chat interactivo'));
136
+ console.log(ui_1.theme.muted(' plazbot whatsapp assign <phone> <agentId> Asignar a un agente'));
137
+ console.log();
138
+ }
139
+ else {
140
+ pollSpinner.warn(chalk_1.default.hex('#FFA726')('Timeout: no se detecto conexion en 5 minutos.'));
141
+ console.log(ui_1.theme.muted(' Puedes volver a intentar con: plazbot whatsapp connect'));
142
+ console.log();
143
+ }
144
+ }
145
+ catch (error) {
146
+ const message = error instanceof Error ? error.message : 'Error desconocido';
147
+ logger_1.logger.error(message);
148
+ process.exit(1);
149
+ }
150
+ });
@@ -8,10 +8,12 @@ const register_webhook_1 = require("./register-webhook");
8
8
  const delete_webhook_1 = require("./delete-webhook");
9
9
  const broadcast_1 = require("./broadcast");
10
10
  const chat_1 = require("./chat");
11
+ const connect_1 = require("./connect");
11
12
  const widget_1 = require("./widget");
12
13
  const channels_1 = require("./channels");
13
14
  exports.whatsappCommands = new commander_1.Command('whatsapp')
14
15
  .description('Comandos de WhatsApp: mensajes, templates, broadcast y mas')
16
+ .addCommand(connect_1.whatsappConnectCommand)
15
17
  .addCommand(send_1.sendMessageCommand)
16
18
  .addCommand(send_template_1.sendTemplateCommand)
17
19
  .addCommand(register_webhook_1.registerWebhookCommand)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plazbot-cli",
3
- "version": "0.2.23",
3
+ "version": "0.2.24",
4
4
  "description": "CLI para Plazbot SDK",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -0,0 +1,168 @@
1
+ import { Command } from 'commander';
2
+ import axios from 'axios';
3
+ import ora from 'ora';
4
+ import chalk from 'chalk';
5
+ import { getStoredCredentials } from '../../utils/credentials';
6
+ import { logger } from '../../utils/logger';
7
+ import { theme, section } from '../../utils/ui';
8
+
9
+ function getBaseUrl(zone: string, dev: boolean): string {
10
+ if (dev) return 'http://localhost:5090';
11
+ return zone === 'EU' ? 'https://apieu.plazbot.com' : 'https://api.plazbot.com';
12
+ }
13
+
14
+ function getIntegrationLabel(ig: any): string {
15
+ if (ig.type === 'whatsapp') {
16
+ return ig.aliasName
17
+ ? `${ig.aliasName} (${ig.cellphoneNumberFormat || ig.cellphoneNumber || ''})`
18
+ : ig.cellphoneNumberFormat || ig.cellphoneNumber || ig.id;
19
+ }
20
+ return ig.aliasName || ig.srcName || ig.id;
21
+ }
22
+
23
+ async function fetchWorkspaceIntegrations(baseUrl: string, workspaceId: string, headers: any): Promise<any[]> {
24
+ try {
25
+ const wkRes = await axios.get(`${baseUrl}/api/workspace/${workspaceId}`, { headers });
26
+ const wsData = Array.isArray(wkRes.data) ? wkRes.data[0] : wkRes.data;
27
+ return wsData?.integrations || [];
28
+ } catch {
29
+ return [];
30
+ }
31
+ }
32
+
33
+ async function activateIntegration(baseUrl: string, workspaceId: string, integrationId: string, headers: any): Promise<boolean> {
34
+ try {
35
+ const res = await axios.post(
36
+ `${baseUrl}/api/workspace/${workspaceId}/integrations/${integrationId}/activate`,
37
+ { isActive: true },
38
+ { headers }
39
+ );
40
+ return res.data?.success === true;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+
46
+ export const whatsappConnectCommand = new Command('connect')
47
+ .description('Conectar un numero de WhatsApp al workspace (sin crear agente)')
48
+ .option('--dev', 'Usar ambiente de desarrollo', false)
49
+ .option('--business', 'Conectar como WhatsApp Business', false)
50
+ .action(async (options: any) => {
51
+ try {
52
+ const credentials = await getStoredCredentials();
53
+ const baseUrl = getBaseUrl(credentials.zone, options.dev);
54
+ const headers = {
55
+ 'Authorization': `Bearer ${credentials.apiKey}`,
56
+ 'x-workspace-id': credentials.workspace,
57
+ 'Content-Type': 'application/json',
58
+ };
59
+
60
+ const channelType = options.business ? 'whatsapp_business' : 'whatsapp';
61
+ const channelLabel = options.business ? 'WhatsApp Business' : 'WhatsApp';
62
+
63
+ console.log(section(`Conectar ${channelLabel}`));
64
+ console.log();
65
+
66
+ // Cargar integraciones existentes
67
+ const loadSpinner = ora({ text: chalk.gray('Cargando integraciones existentes...'), spinner: 'dots', color: 'green' }).start();
68
+ const allIntegrations = await fetchWorkspaceIntegrations(baseUrl, credentials.workspace, headers);
69
+ const existing = allIntegrations.filter((ig: any) => ig.type === 'whatsapp');
70
+ loadSpinner.stop();
71
+
72
+ if (existing.length > 0) {
73
+ console.log(theme.muted(` ${existing.length} numero(s) ya conectado(s):`));
74
+ existing.forEach((ig: any) => {
75
+ const label = getIntegrationLabel(ig);
76
+ const status = ig.isActive ? chalk.hex('#66BB6A')('activo') : chalk.hex('#EF5350')('inactivo');
77
+ console.log(` ${theme.muted('•')} ${label} ${status}`);
78
+ });
79
+ console.log();
80
+ }
81
+
82
+ // Generar link de onboarding
83
+ const spinner = ora({ text: chalk.gray('Generando link de conexion...'), spinner: 'dots', color: 'green' }).start();
84
+
85
+ const linkRes = await axios.post(
86
+ `${baseUrl}/api/workspace/${credentials.workspace}/onboarding-link`,
87
+ { type: channelType },
88
+ { headers }
89
+ );
90
+
91
+ if (!linkRes.data?.success) {
92
+ spinner.fail(chalk.hex('#EF5350')(`Error: ${linkRes.data?.message || 'No se pudo generar el link'}`));
93
+ process.exit(1);
94
+ }
95
+
96
+ const shortUrl = linkRes.data.data?.shortUrl || '';
97
+ spinner.stop();
98
+
99
+ // Mostrar link
100
+ console.log(chalk.hex('#25D366')(' ┌' + '─'.repeat(54) + '┐'));
101
+ console.log(chalk.hex('#25D366')(' │') + chalk.bold(` Abre este link en tu navegador:`).padEnd(64) + chalk.hex('#25D366')('│'));
102
+ console.log(chalk.hex('#25D366')(' │') + ''.padEnd(54) + chalk.hex('#25D366')('│'));
103
+ console.log(chalk.hex('#25D366')(' │') + chalk.hex('#22d3ee')(` ${shortUrl}`).padEnd(64) + chalk.hex('#25D366')('│'));
104
+ console.log(chalk.hex('#25D366')(' │') + ''.padEnd(54) + chalk.hex('#25D366')('│'));
105
+ console.log(chalk.hex('#25D366')(' │') + chalk.gray(' Escanea el codigo QR desde tu WhatsApp').padEnd(64) + chalk.hex('#25D366')('│'));
106
+ console.log(chalk.hex('#25D366')(' └' + '─'.repeat(54) + '┘'));
107
+ console.log();
108
+
109
+ // Guardar IDs actuales para detectar nuevos
110
+ const prevIds = new Set(allIntegrations.map((i: any) => i.id));
111
+
112
+ // Polling para detectar nueva integracion
113
+ const pollSpinner = ora({ text: chalk.gray('Esperando conexion... (Ctrl+C para cancelar)'), spinner: 'dots', color: 'cyan' }).start();
114
+ const POLL_INTERVAL = 5000;
115
+ const MAX_POLLS = 60; // 5 minutos
116
+ let detected: any = null;
117
+
118
+ for (let i = 0; i < MAX_POLLS; i++) {
119
+ await new Promise(r => setTimeout(r, POLL_INTERVAL));
120
+
121
+ try {
122
+ const freshIntegrations = await fetchWorkspaceIntegrations(baseUrl, credentials.workspace, headers);
123
+ const newOnes = freshIntegrations.filter((ig: any) => !prevIds.has(ig.id));
124
+ if (newOnes.length > 0) {
125
+ const match = newOnes.find((ig: any) => ig.type === 'whatsapp');
126
+ detected = match || newOnes[0];
127
+ break;
128
+ }
129
+ } catch {}
130
+
131
+ const elapsed = Math.floor(((i + 1) * POLL_INTERVAL) / 1000);
132
+ pollSpinner.text = chalk.gray(`Esperando conexion... ${elapsed}s (Ctrl+C para cancelar)`);
133
+ }
134
+
135
+ if (detected) {
136
+ const detectedName = getIntegrationLabel(detected);
137
+ pollSpinner.succeed(chalk.hex('#4ade80')(`Conexion detectada: ${detectedName}`));
138
+
139
+ // Activar la integracion
140
+ const actSpinner = ora({ text: chalk.gray('Activando integracion...'), spinner: 'dots', color: 'green' }).start();
141
+ const activated = await activateIntegration(baseUrl, credentials.workspace, detected.id, headers);
142
+ if (activated) {
143
+ actSpinner.succeed(chalk.hex('#4ade80')('Integracion activada correctamente'));
144
+ } else {
145
+ actSpinner.warn(chalk.hex('#FFA726')('No se pudo activar automaticamente. Activala desde la plataforma.'));
146
+ }
147
+
148
+ console.log();
149
+ console.log(theme.success(' WhatsApp conectado exitosamente'));
150
+ console.log(theme.muted(` Numero: ${detectedName}`));
151
+ console.log();
152
+ console.log(theme.muted(' Ahora puedes:'));
153
+ console.log(theme.muted(' plazbot whatsapp send-message Enviar un mensaje'));
154
+ console.log(theme.muted(' plazbot whatsapp chat <phone> Chat interactivo'));
155
+ console.log(theme.muted(' plazbot whatsapp assign <phone> <agentId> Asignar a un agente'));
156
+ console.log();
157
+ } else {
158
+ pollSpinner.warn(chalk.hex('#FFA726')('Timeout: no se detecto conexion en 5 minutos.'));
159
+ console.log(theme.muted(' Puedes volver a intentar con: plazbot whatsapp connect'));
160
+ console.log();
161
+ }
162
+
163
+ } catch (error) {
164
+ const message = error instanceof Error ? error.message : 'Error desconocido';
165
+ logger.error(message);
166
+ process.exit(1);
167
+ }
168
+ });
@@ -5,11 +5,13 @@ import { registerWebhookCommand } from './register-webhook';
5
5
  import { deleteWebhookCommand } from './delete-webhook';
6
6
  import { broadcastCommand } from './broadcast';
7
7
  import { whatsappChatCommand } from './chat';
8
+ import { whatsappConnectCommand } from './connect';
8
9
  import { widgetCommand } from './widget';
9
10
  import { channelsCommand, assignCommand } from './channels';
10
11
 
11
12
  export const whatsappCommands = new Command('whatsapp')
12
13
  .description('Comandos de WhatsApp: mensajes, templates, broadcast y mas')
14
+ .addCommand(whatsappConnectCommand)
13
15
  .addCommand(sendMessageCommand)
14
16
  .addCommand(sendTemplateCommand)
15
17
  .addCommand(registerWebhookCommand)