plazbot-cli 0.2.19 → 0.2.21
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/dist/cli.js +3 -0
- package/dist/commands/agent/templates.js +29 -24
- package/dist/commands/agent/wizard.js +1 -0
- package/dist/commands/workers/deploy.js +127 -0
- package/dist/commands/workers/index.js +18 -0
- package/dist/commands/workers/list.js +81 -0
- package/dist/commands/workers/logs.js +78 -0
- package/dist/commands/workers/remove.js +88 -0
- package/dist/commands/workers/secret.js +151 -0
- package/dist/commands/workers/test.js +109 -0
- package/dist/types/workers.js +2 -0
- package/dist/utils/api.js +25 -0
- package/package.json +2 -2
- package/src/cli.ts +4 -0
- package/src/commands/agent/templates.ts +28 -25
- package/src/commands/agent/wizard.ts +2 -2
- package/src/commands/workers/deploy.ts +144 -0
- package/src/commands/workers/index.ts +16 -0
- package/src/commands/workers/list.ts +93 -0
- package/src/commands/workers/logs.ts +89 -0
- package/src/commands/workers/remove.ts +95 -0
- package/src/commands/workers/secret.ts +179 -0
- package/src/commands/workers/test.ts +133 -0
- package/src/types/workers.ts +86 -0
- package/src/utils/api.ts +29 -0
|
@@ -0,0 +1,151 @@
|
|
|
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.secretCommand = void 0;
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const credentials_1 = require("../../utils/credentials");
|
|
9
|
+
const api_1 = require("../../utils/api");
|
|
10
|
+
const logger_1 = require("../../utils/logger");
|
|
11
|
+
const ui_1 = require("../../utils/ui");
|
|
12
|
+
const readline_1 = __importDefault(require("readline"));
|
|
13
|
+
// ── Comando padre: secret ───────────────────────────────────────────────────
|
|
14
|
+
exports.secretCommand = new commander_1.Command('secret')
|
|
15
|
+
.description('Gestionar secrets (variables de entorno) de los workers')
|
|
16
|
+
.addCommand(secretSetCommand())
|
|
17
|
+
.addCommand(secretListCommand())
|
|
18
|
+
.addCommand(secretRemoveCommand());
|
|
19
|
+
// ── secret set ──────────────────────────────────────────────────────────────
|
|
20
|
+
function secretSetCommand() {
|
|
21
|
+
return new commander_1.Command('set')
|
|
22
|
+
.description('Crear o actualizar un secret')
|
|
23
|
+
.argument('<key>', 'Nombre del secret (ej: STRIPE_API_KEY)')
|
|
24
|
+
.argument('<value>', 'Valor del secret')
|
|
25
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
26
|
+
.action(async (key, value, options) => {
|
|
27
|
+
try {
|
|
28
|
+
const credentials = await (0, credentials_1.getStoredCredentials)();
|
|
29
|
+
const api = (0, api_1.createApiClient)({
|
|
30
|
+
apiKey: credentials.apiKey,
|
|
31
|
+
workspace: credentials.workspace,
|
|
32
|
+
zone: credentials.zone,
|
|
33
|
+
dev: options.dev,
|
|
34
|
+
});
|
|
35
|
+
const spinner = (0, ui_1.createSpinner)(`Guardando secret "${key}"...`);
|
|
36
|
+
spinner.start();
|
|
37
|
+
await api.put('/api/workers/secrets', { key, value });
|
|
38
|
+
spinner.succeed(`Secret "${key}" guardado`);
|
|
39
|
+
logger_1.logger.dim(` Disponible en tus workers como plz.env.${key}`);
|
|
40
|
+
console.log();
|
|
41
|
+
if (options.dev) {
|
|
42
|
+
logger_1.logger.warning('Ambiente: desarrollo');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
const message = error instanceof Error ? error.message : 'Error desconocido';
|
|
47
|
+
logger_1.logger.error(message);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// ── secret list ─────────────────────────────────────────────────────────────
|
|
53
|
+
function secretListCommand() {
|
|
54
|
+
return new commander_1.Command('list')
|
|
55
|
+
.description('Lista todos los secrets del workspace (sin mostrar valores)')
|
|
56
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
57
|
+
.action(async (options) => {
|
|
58
|
+
try {
|
|
59
|
+
const credentials = await (0, credentials_1.getStoredCredentials)();
|
|
60
|
+
const api = (0, api_1.createApiClient)({
|
|
61
|
+
apiKey: credentials.apiKey,
|
|
62
|
+
workspace: credentials.workspace,
|
|
63
|
+
zone: credentials.zone,
|
|
64
|
+
dev: options.dev,
|
|
65
|
+
});
|
|
66
|
+
const spinner = (0, ui_1.createSpinner)('Obteniendo secrets...');
|
|
67
|
+
spinner.start();
|
|
68
|
+
const response = await api.get('/api/workers/secrets');
|
|
69
|
+
const secrets = response.data;
|
|
70
|
+
spinner.stop();
|
|
71
|
+
if (!secrets || secrets.length === 0) {
|
|
72
|
+
logger_1.logger.info('\n No hay secrets configurados');
|
|
73
|
+
logger_1.logger.dim(' Agrega uno: plazbot workers secret set MI_KEY mi_valor');
|
|
74
|
+
console.log();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
logger_1.logger.title(`Secrets (${secrets.length})`);
|
|
78
|
+
const rows = secrets.map(s => [
|
|
79
|
+
s.key,
|
|
80
|
+
ui_1.theme.muted('••••••••'),
|
|
81
|
+
(0, ui_1.formatDate)(s.updatedAt),
|
|
82
|
+
]);
|
|
83
|
+
console.log((0, ui_1.createTable)(['Key', 'Valor', 'Actualizado'], rows));
|
|
84
|
+
console.log();
|
|
85
|
+
if (options.dev) {
|
|
86
|
+
logger_1.logger.warning('Ambiente: desarrollo');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
const message = error instanceof Error ? error.message : 'Error desconocido';
|
|
91
|
+
logger_1.logger.error(message);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// ── secret remove ───────────────────────────────────────────────────────────
|
|
97
|
+
function secretRemoveCommand() {
|
|
98
|
+
return new commander_1.Command('remove')
|
|
99
|
+
.description('Elimina un secret del workspace')
|
|
100
|
+
.argument('<key>', 'Nombre del secret a eliminar')
|
|
101
|
+
.option('-f, --force', 'Eliminar sin confirmacion', false)
|
|
102
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
103
|
+
.action(async (key, options) => {
|
|
104
|
+
try {
|
|
105
|
+
const credentials = await (0, credentials_1.getStoredCredentials)();
|
|
106
|
+
const api = (0, api_1.createApiClient)({
|
|
107
|
+
apiKey: credentials.apiKey,
|
|
108
|
+
workspace: credentials.workspace,
|
|
109
|
+
zone: credentials.zone,
|
|
110
|
+
dev: options.dev,
|
|
111
|
+
});
|
|
112
|
+
if (!options.force) {
|
|
113
|
+
const confirmed = await askConfirmation(` Eliminar secret "${key}"? (y/N): `);
|
|
114
|
+
if (!confirmed) {
|
|
115
|
+
logger_1.logger.info('\n Operacion cancelada');
|
|
116
|
+
process.exit(0);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const spinner = (0, ui_1.createSpinner)(`Eliminando secret "${key}"...`);
|
|
120
|
+
spinner.start();
|
|
121
|
+
await api.delete(`/api/workers/secrets/${encodeURIComponent(key)}`);
|
|
122
|
+
spinner.succeed(`Secret "${key}" eliminado`);
|
|
123
|
+
console.log();
|
|
124
|
+
if (options.dev) {
|
|
125
|
+
logger_1.logger.warning('Ambiente: desarrollo');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (error.response?.status === 404) {
|
|
130
|
+
logger_1.logger.error(`Secret "${key}" no encontrado`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const message = error instanceof Error ? error.message : 'Error desconocido';
|
|
134
|
+
logger_1.logger.error(message);
|
|
135
|
+
}
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
function askConfirmation(question) {
|
|
141
|
+
return new Promise((resolve) => {
|
|
142
|
+
const rl = readline_1.default.createInterface({
|
|
143
|
+
input: process.stdin,
|
|
144
|
+
output: process.stdout,
|
|
145
|
+
});
|
|
146
|
+
rl.question(question, (answer) => {
|
|
147
|
+
rl.close();
|
|
148
|
+
resolve(answer.toLowerCase() === 'y');
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.testCommand = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const credentials_1 = require("../../utils/credentials");
|
|
6
|
+
const api_1 = require("../../utils/api");
|
|
7
|
+
const logger_1 = require("../../utils/logger");
|
|
8
|
+
const ui_1 = require("../../utils/ui");
|
|
9
|
+
exports.testCommand = new commander_1.Command('test')
|
|
10
|
+
.description('Ejecutar un worker manualmente para probarlo')
|
|
11
|
+
.argument('<name>', 'Nombre del worker a ejecutar')
|
|
12
|
+
.option('-p, --payload <json>', 'Payload JSON para el worker (ej: \'{"producto":"iPhone"}\')')
|
|
13
|
+
.option('-t, --type <type>', 'Tipo del worker (tool, worker, sync, schedule, webhook)')
|
|
14
|
+
.option('--contact <id>', 'ID del contacto (contexto para tools)')
|
|
15
|
+
.option('--agent <id>', 'ID del agente (contexto para tools)')
|
|
16
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
17
|
+
.action(async (name, options) => {
|
|
18
|
+
try {
|
|
19
|
+
const credentials = await (0, credentials_1.getStoredCredentials)();
|
|
20
|
+
const api = (0, api_1.createApiClient)({
|
|
21
|
+
apiKey: credentials.apiKey,
|
|
22
|
+
workspace: credentials.workspace,
|
|
23
|
+
zone: credentials.zone,
|
|
24
|
+
dev: options.dev,
|
|
25
|
+
});
|
|
26
|
+
// Parsear payload
|
|
27
|
+
let payload = {};
|
|
28
|
+
if (options.payload) {
|
|
29
|
+
try {
|
|
30
|
+
payload = JSON.parse(options.payload);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
logger_1.logger.error('El payload no es JSON valido');
|
|
34
|
+
logger_1.logger.dim('Ejemplo: --payload \'{"producto":"iPhone 15"}\'');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
logger_1.logger.title(`Ejecutando worker "${name}"`);
|
|
39
|
+
console.log();
|
|
40
|
+
if (Object.keys(payload).length > 0) {
|
|
41
|
+
logger_1.logger.label('Payload', JSON.stringify(payload, null, 2));
|
|
42
|
+
console.log();
|
|
43
|
+
}
|
|
44
|
+
const spinner = (0, ui_1.createSpinner)('Ejecutando...');
|
|
45
|
+
spinner.start();
|
|
46
|
+
const response = await api.post('/api/worker/execute', {
|
|
47
|
+
workerName: name,
|
|
48
|
+
type: options.type,
|
|
49
|
+
payload,
|
|
50
|
+
triggerSource: 'cli-test',
|
|
51
|
+
agentId: options.agent,
|
|
52
|
+
contactId: options.contact,
|
|
53
|
+
});
|
|
54
|
+
const data = response.data;
|
|
55
|
+
const duration = data.duration ?? 0;
|
|
56
|
+
spinner.stop();
|
|
57
|
+
if (data.success) {
|
|
58
|
+
logger_1.logger.success(`Ejecutado en ${duration}ms`);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
logger_1.logger.error(`Fallo en ${duration}ms`);
|
|
62
|
+
}
|
|
63
|
+
console.log();
|
|
64
|
+
// Mostrar resultado
|
|
65
|
+
if (data.result !== undefined && data.result !== null) {
|
|
66
|
+
logger_1.logger.label('Resultado', '');
|
|
67
|
+
const result = data.result;
|
|
68
|
+
// Si es un ToolResponse, mostrar formateado
|
|
69
|
+
if (typeof result === 'object' && 'result' in result) {
|
|
70
|
+
console.log();
|
|
71
|
+
logger_1.logger.label(' result', ui_1.theme.success(String(result.result)));
|
|
72
|
+
if (result.data) {
|
|
73
|
+
logger_1.logger.label(' data', JSON.stringify(result.data, null, 2));
|
|
74
|
+
}
|
|
75
|
+
if (Array.isArray(result.actions) && result.actions.length > 0) {
|
|
76
|
+
logger_1.logger.label(' actions', '');
|
|
77
|
+
for (const action of result.actions) {
|
|
78
|
+
const a = action;
|
|
79
|
+
logger_1.logger.dim(` - ${a.type}: ${a.value}${a.code ? ` (${a.code})` : ''}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Resultado generico
|
|
85
|
+
console.log(JSON.stringify(result, null, 2));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Mostrar error si hubo
|
|
89
|
+
if (data.error) {
|
|
90
|
+
console.log();
|
|
91
|
+
logger_1.logger.label('Error', ui_1.theme.error(data.error));
|
|
92
|
+
}
|
|
93
|
+
console.log();
|
|
94
|
+
if (options.dev) {
|
|
95
|
+
logger_1.logger.warning('Ambiente: desarrollo');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (error.response?.status === 404) {
|
|
100
|
+
logger_1.logger.error(`Worker "${name}" no encontrado o inactivo`);
|
|
101
|
+
logger_1.logger.dim('Verifica que el worker este desplegado con: plazbot workers list');
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
const message = error.response?.data?.message || error.message || 'Error desconocido';
|
|
105
|
+
logger_1.logger.error(message);
|
|
106
|
+
}
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Utilidades para llamadas directas al API de Plazbot
|
|
3
|
+
// Usado por comandos que no tienen clase SDK (ej: workers)
|
|
4
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
|
+
};
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.createApiClient = createApiClient;
|
|
9
|
+
const axios_1 = __importDefault(require("axios"));
|
|
10
|
+
function getBaseUrl(zone, dev) {
|
|
11
|
+
if (dev)
|
|
12
|
+
return 'http://localhost:5090';
|
|
13
|
+
return zone === 'EU' ? 'https://apieu.plazbot.com' : 'https://api.plazbot.com';
|
|
14
|
+
}
|
|
15
|
+
function createApiClient(options) {
|
|
16
|
+
const baseURL = getBaseUrl(options.zone, options.dev);
|
|
17
|
+
return axios_1.default.create({
|
|
18
|
+
baseURL,
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
'x-api-key': options.apiKey,
|
|
22
|
+
'x-workspace-id': options.workspace,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plazbot-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.21",
|
|
4
4
|
"description": "CLI para Plazbot SDK",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"marked": "^12.0.2",
|
|
45
45
|
"marked-terminal": "^7.2.1",
|
|
46
46
|
"ora": "^5.4.1",
|
|
47
|
-
"plazbot": "^2.
|
|
47
|
+
"plazbot": "^2.1.4"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/node": "^20.0.0",
|
package/src/cli.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { portalCommands } from './commands/portal';
|
|
|
4
4
|
import { authCommands } from './commands/auth';
|
|
5
5
|
import { agentCommands } from './commands/agent';
|
|
6
6
|
import { whatsappCommands } from './commands/whatsapp';
|
|
7
|
+
import { workersCommands } from './commands/workers';
|
|
7
8
|
import { showBanner } from './utils/banner';
|
|
8
9
|
|
|
9
10
|
// Configuracion basica del CLI
|
|
@@ -24,6 +25,9 @@ program.addCommand(portalCommands);
|
|
|
24
25
|
// Registrar todos los comandos de WhatsApp
|
|
25
26
|
program.addCommand(whatsappCommands);
|
|
26
27
|
|
|
28
|
+
// Registrar todos los comandos de Workers
|
|
29
|
+
program.addCommand(workersCommands);
|
|
30
|
+
|
|
27
31
|
// Si no se pasan argumentos, mostrar banner
|
|
28
32
|
if (process.argv.length <= 2) {
|
|
29
33
|
showBanner().then(() => {
|
|
@@ -5,6 +5,7 @@ import { getStoredCredentials } from '../../utils/credentials';
|
|
|
5
5
|
import { logger } from '../../utils/logger';
|
|
6
6
|
import { theme, section, kvPair, createTable, createSpinner } from '../../utils/ui';
|
|
7
7
|
import { AgentCommandOptions } from '../../types/agent';
|
|
8
|
+
import { connectChannelFlow, WizardContext } from './wizard';
|
|
8
9
|
|
|
9
10
|
interface TemplatePlaceholder {
|
|
10
11
|
key: string;
|
|
@@ -727,24 +728,25 @@ export const templatesCommand = new Command('templates')
|
|
|
727
728
|
placeholderAnswers[ph.key] = value.trim();
|
|
728
729
|
}
|
|
729
730
|
|
|
730
|
-
// Paso 3: WhatsApp
|
|
731
|
-
console.log(theme.bold('\n
|
|
732
|
-
const {
|
|
731
|
+
// Paso 3: Canales (WhatsApp, Messenger, Instagram, Telegram)
|
|
732
|
+
console.log(theme.bold('\n Conexion de canales'));
|
|
733
|
+
const { wantChannels } = await (inquirer as any).prompt([{
|
|
733
734
|
type: 'confirm',
|
|
734
|
-
name: '
|
|
735
|
-
message: 'Deseas
|
|
735
|
+
name: 'wantChannels',
|
|
736
|
+
message: 'Deseas conectar canales (WhatsApp, Messenger, Instagram, Telegram)?',
|
|
736
737
|
default: true,
|
|
737
738
|
}]);
|
|
738
739
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
740
|
+
const credentials = await getStoredCredentials();
|
|
741
|
+
let connectedChannels: { channel: string; key: string; multianswer: boolean }[] = [];
|
|
742
|
+
if (wantChannels) {
|
|
743
|
+
const wizardCtx: WizardContext = {
|
|
744
|
+
zone: credentials.zone,
|
|
745
|
+
workspaceId: credentials.workspace,
|
|
746
|
+
apiKey: credentials.apiKey,
|
|
747
|
+
dev: !!options.dev,
|
|
748
|
+
};
|
|
749
|
+
connectedChannels = await connectChannelFlow(wizardCtx);
|
|
748
750
|
}
|
|
749
751
|
|
|
750
752
|
// Paso 4: API Key de OpenAI
|
|
@@ -782,8 +784,8 @@ export const templatesCommand = new Command('templates')
|
|
|
782
784
|
|
|
783
785
|
finalConfig.timezone = timezone;
|
|
784
786
|
|
|
785
|
-
if (
|
|
786
|
-
finalConfig.channels =
|
|
787
|
+
if (connectedChannels.length > 0) {
|
|
788
|
+
finalConfig.channels = connectedChannels;
|
|
787
789
|
} else {
|
|
788
790
|
finalConfig.channels = [];
|
|
789
791
|
}
|
|
@@ -805,7 +807,7 @@ export const templatesCommand = new Command('templates')
|
|
|
805
807
|
for (const ph of template.placeholders) {
|
|
806
808
|
console.log(kvPair(ph.label, placeholderAnswers[ph.key]));
|
|
807
809
|
}
|
|
808
|
-
console.log(kvPair('
|
|
810
|
+
console.log(kvPair('Canales', connectedChannels.length > 0 ? connectedChannels.map(c => `${c.channel} (${c.key})`).join(', ') : 'No configurado'));
|
|
809
811
|
console.log(kvPair('API Key OpenAI', apiToken ? 'Configurada' : 'No configurada (el agente no funcionara sin ella)'));
|
|
810
812
|
console.log(kvPair('Zona horaria', timezone));
|
|
811
813
|
console.log(kvPair('Acciones', String(finalConfig.actions.length)));
|
|
@@ -825,8 +827,6 @@ export const templatesCommand = new Command('templates')
|
|
|
825
827
|
}
|
|
826
828
|
|
|
827
829
|
// Paso 7: Crear agente
|
|
828
|
-
const credentials = await getStoredCredentials();
|
|
829
|
-
|
|
830
830
|
const agent = new Agent({
|
|
831
831
|
workspaceId: credentials.workspace,
|
|
832
832
|
apiKey: credentials.apiKey,
|
|
@@ -849,17 +849,20 @@ export const templatesCommand = new Command('templates')
|
|
|
849
849
|
}
|
|
850
850
|
logger.label('Nombre', finalConfig.name);
|
|
851
851
|
logger.label('Industria', template.industry);
|
|
852
|
-
if (
|
|
853
|
-
logger.label('
|
|
852
|
+
if (connectedChannels.length > 0) {
|
|
853
|
+
logger.label('Canales', connectedChannels.map(c => `${c.channel} (${c.key})`).join(', '));
|
|
854
854
|
}
|
|
855
855
|
logger.label('Acciones', String(finalConfig.actions.length));
|
|
856
856
|
|
|
857
857
|
console.log();
|
|
858
858
|
console.log(section('Siguientes pasos'));
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
859
|
+
if (connectedChannels.length === 0) {
|
|
860
|
+
console.log(theme.bold(' 1.') + ' Conecta canales desde: ' + theme.success('plazbot agent chat -a <agentId>'));
|
|
861
|
+
console.log(theme.bold(' 2.') + ' O conecta desde la plataforma en Ajustes > Integraciones');
|
|
862
|
+
} else {
|
|
863
|
+
console.log(theme.bold(' 1.') + ' Tu agente ya tiene canales conectados y activados');
|
|
864
|
+
}
|
|
865
|
+
console.log(theme.bold(` ${connectedChannels.length === 0 ? '3' : '2'}.`) + ' Agrega tu API Key de OpenAI si no la configuraste');
|
|
863
866
|
console.log();
|
|
864
867
|
|
|
865
868
|
if (!apiToken) {
|
|
@@ -96,7 +96,7 @@ const MODELS: Record<string, string[]> = {
|
|
|
96
96
|
|
|
97
97
|
const COLORS = ['blue', 'green', 'orange', 'gray', 'white'];
|
|
98
98
|
|
|
99
|
-
interface WizardContext {
|
|
99
|
+
export interface WizardContext {
|
|
100
100
|
zone: string;
|
|
101
101
|
workspaceId: string;
|
|
102
102
|
apiKey: string;
|
|
@@ -160,7 +160,7 @@ async function activateIntegration(baseUrl: string, workspaceId: string, integra
|
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
async function connectChannelFlow(ctx: WizardContext): Promise<AgentChannel[]> {
|
|
163
|
+
export async function connectChannelFlow(ctx: WizardContext): Promise<AgentChannel[]> {
|
|
164
164
|
const channels: AgentChannel[] = [];
|
|
165
165
|
const baseUrl = getBaseUrl(ctx.zone, ctx.dev);
|
|
166
166
|
const headers = {
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { getStoredCredentials } from '../../utils/credentials';
|
|
5
|
+
import { createApiClient } from '../../utils/api';
|
|
6
|
+
import { logger } from '../../utils/logger';
|
|
7
|
+
import { createSpinner, createTable, theme } from '../../utils/ui';
|
|
8
|
+
import { WorkerDeployOptions, WorkerDeployResult } from '../../types/workers';
|
|
9
|
+
|
|
10
|
+
export const deployCommand = new Command('deploy')
|
|
11
|
+
.description('Despliega un worker al workspace de Plazbot')
|
|
12
|
+
.argument('[file]', 'Archivo TypeScript del worker (ej: ./workers/mi-tool.ts)')
|
|
13
|
+
.option('-n, --name <name>', 'Nombre del worker (sobreescribe el del archivo)')
|
|
14
|
+
.option('-d, --description <desc>', 'Descripcion del worker')
|
|
15
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
16
|
+
.action(async (file: string | undefined, options: WorkerDeployOptions) => {
|
|
17
|
+
try {
|
|
18
|
+
const credentials = await getStoredCredentials();
|
|
19
|
+
|
|
20
|
+
// Buscar archivos de workers
|
|
21
|
+
const files = await resolveWorkerFiles(file);
|
|
22
|
+
|
|
23
|
+
if (files.length === 0) {
|
|
24
|
+
logger.error('No se encontraron archivos de workers para desplegar');
|
|
25
|
+
logger.dim('Uso: plazbot workers deploy ./workers/mi-tool.ts');
|
|
26
|
+
logger.dim(' o: plazbot workers deploy (busca en ./workers/ automaticamente)');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const api = createApiClient({
|
|
31
|
+
apiKey: credentials.apiKey,
|
|
32
|
+
workspace: credentials.workspace,
|
|
33
|
+
zone: credentials.zone,
|
|
34
|
+
dev: options.dev,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
logger.title(`Desplegando ${files.length} worker${files.length > 1 ? 's' : ''}`);
|
|
38
|
+
console.log();
|
|
39
|
+
|
|
40
|
+
const results: { file: string; result?: WorkerDeployResult; error?: string }[] = [];
|
|
41
|
+
|
|
42
|
+
for (const filePath of files) {
|
|
43
|
+
const fileName = path.basename(filePath);
|
|
44
|
+
const spinner = createSpinner(`Desplegando ${fileName}...`);
|
|
45
|
+
spinner.start();
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
// Leer archivo fuente
|
|
49
|
+
const sourceCode = await fs.readFile(filePath, 'utf-8');
|
|
50
|
+
|
|
51
|
+
// Enviar al API
|
|
52
|
+
const response = await api.post('/api/workers/deploy', {
|
|
53
|
+
sourceCode,
|
|
54
|
+
fileName,
|
|
55
|
+
name: files.length === 1 ? options.name : undefined,
|
|
56
|
+
description: files.length === 1 ? options.description : undefined,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const data = response.data as WorkerDeployResult;
|
|
60
|
+
spinner.succeed(`${fileName} → ${data.name} (${data.type})`);
|
|
61
|
+
results.push({ file: fileName, result: data });
|
|
62
|
+
|
|
63
|
+
} catch (error: any) {
|
|
64
|
+
const msg = error.response?.data?.message || error.message || 'Error desconocido';
|
|
65
|
+
spinner.fail(`${fileName}: ${msg}`);
|
|
66
|
+
results.push({ file: fileName, error: msg });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Resumen
|
|
71
|
+
console.log();
|
|
72
|
+
const success = results.filter(r => r.result);
|
|
73
|
+
const failed = results.filter(r => r.error);
|
|
74
|
+
|
|
75
|
+
if (success.length > 0) {
|
|
76
|
+
logger.success(`${success.length} worker${success.length > 1 ? 's' : ''} desplegado${success.length > 1 ? 's' : ''}`);
|
|
77
|
+
|
|
78
|
+
const rows = success.map(r => [
|
|
79
|
+
r.result!.name,
|
|
80
|
+
r.result!.type,
|
|
81
|
+
r.result!.status,
|
|
82
|
+
r.result!.webhookUrl || r.result!.cronExpression || '—',
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
console.log(createTable(
|
|
86
|
+
['Nombre', 'Tipo', 'Estado', 'URL/Cron'],
|
|
87
|
+
rows,
|
|
88
|
+
));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (failed.length > 0) {
|
|
92
|
+
logger.warning(`${failed.length} worker${failed.length > 1 ? 's' : ''} fallaron`);
|
|
93
|
+
failed.forEach(f => logger.dim(` ${f.file}: ${f.error}`));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Mostrar webhook URLs si hay
|
|
97
|
+
const webhooks = success.filter(r => r.result!.webhookUrl);
|
|
98
|
+
if (webhooks.length > 0) {
|
|
99
|
+
console.log();
|
|
100
|
+
logger.title('Webhook URLs');
|
|
101
|
+
webhooks.forEach(w => {
|
|
102
|
+
logger.label(w.result!.name, theme.secondary(w.result!.webhookUrl!));
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log();
|
|
107
|
+
|
|
108
|
+
if (options.dev) {
|
|
109
|
+
logger.warning('Ambiente: desarrollo');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
} catch (error) {
|
|
113
|
+
const message = error instanceof Error ? error.message : 'Error desconocido';
|
|
114
|
+
logger.error(message);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Resuelve los archivos a desplegar
|
|
120
|
+
async function resolveWorkerFiles(file?: string): Promise<string[]> {
|
|
121
|
+
if (file) {
|
|
122
|
+
// Archivo especifico
|
|
123
|
+
const resolved = path.resolve(file);
|
|
124
|
+
try {
|
|
125
|
+
await fs.access(resolved);
|
|
126
|
+
return [resolved];
|
|
127
|
+
} catch {
|
|
128
|
+
throw new Error(`Archivo no encontrado: ${file}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Buscar en ./workers/ automaticamente
|
|
133
|
+
const workersDir = path.resolve('workers');
|
|
134
|
+
try {
|
|
135
|
+
const entries = await fs.readdir(workersDir);
|
|
136
|
+
const tsFiles = entries
|
|
137
|
+
.filter(e => e.endsWith('.ts') && !e.endsWith('.d.ts'))
|
|
138
|
+
.map(e => path.join(workersDir, e));
|
|
139
|
+
|
|
140
|
+
return tsFiles;
|
|
141
|
+
} catch {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { deployCommand } from './deploy';
|
|
3
|
+
import { listCommand } from './list';
|
|
4
|
+
import { logsCommand } from './logs';
|
|
5
|
+
import { removeCommand } from './remove';
|
|
6
|
+
import { secretCommand } from './secret';
|
|
7
|
+
import { testCommand } from './test';
|
|
8
|
+
|
|
9
|
+
export const workersCommands = new Command('workers')
|
|
10
|
+
.description('Comandos para gestionar workers (tools, syncs, schedules, webhooks)')
|
|
11
|
+
.addCommand(deployCommand)
|
|
12
|
+
.addCommand(listCommand)
|
|
13
|
+
.addCommand(logsCommand)
|
|
14
|
+
.addCommand(removeCommand)
|
|
15
|
+
.addCommand(secretCommand)
|
|
16
|
+
.addCommand(testCommand);
|