plazbot-cli 0.2.20 → 0.2.22
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/auth/index.js +3 -1
- package/dist/commands/auth/status.js +24 -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/auth/index.ts +3 -1
- package/src/commands/auth/status.ts +22 -0
- 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,179 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getStoredCredentials } from '../../utils/credentials';
|
|
3
|
+
import { createApiClient } from '../../utils/api';
|
|
4
|
+
import { logger } from '../../utils/logger';
|
|
5
|
+
import { createSpinner, createTable, theme, formatDate } from '../../utils/ui';
|
|
6
|
+
import { WorkerSecretItem } from '../../types/workers';
|
|
7
|
+
import readline from 'readline';
|
|
8
|
+
|
|
9
|
+
// ── Comando padre: secret ───────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export const secretCommand = new Command('secret')
|
|
12
|
+
.description('Gestionar secrets (variables de entorno) de los workers')
|
|
13
|
+
.addCommand(secretSetCommand())
|
|
14
|
+
.addCommand(secretListCommand())
|
|
15
|
+
.addCommand(secretRemoveCommand());
|
|
16
|
+
|
|
17
|
+
// ── secret set ──────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
function secretSetCommand(): Command {
|
|
20
|
+
return new Command('set')
|
|
21
|
+
.description('Crear o actualizar un secret')
|
|
22
|
+
.argument('<key>', 'Nombre del secret (ej: STRIPE_API_KEY)')
|
|
23
|
+
.argument('<value>', 'Valor del secret')
|
|
24
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
25
|
+
.action(async (key: string, value: string, options: { dev?: boolean }) => {
|
|
26
|
+
try {
|
|
27
|
+
const credentials = await getStoredCredentials();
|
|
28
|
+
const api = createApiClient({
|
|
29
|
+
apiKey: credentials.apiKey,
|
|
30
|
+
workspace: credentials.workspace,
|
|
31
|
+
zone: credentials.zone,
|
|
32
|
+
dev: options.dev,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const spinner = createSpinner(`Guardando secret "${key}"...`);
|
|
36
|
+
spinner.start();
|
|
37
|
+
|
|
38
|
+
await api.put('/api/worker/secrets', { key, value });
|
|
39
|
+
|
|
40
|
+
spinner.succeed(`Secret "${key}" guardado`);
|
|
41
|
+
logger.dim(` Disponible en tus workers como plz.env.${key}`);
|
|
42
|
+
console.log();
|
|
43
|
+
|
|
44
|
+
if (options.dev) {
|
|
45
|
+
logger.warning('Ambiente: desarrollo');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
} catch (error) {
|
|
49
|
+
const message = error instanceof Error ? error.message : 'Error desconocido';
|
|
50
|
+
logger.error(message);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── secret list ─────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
function secretListCommand(): Command {
|
|
59
|
+
return new Command('list')
|
|
60
|
+
.description('Lista todos los secrets del workspace (sin mostrar valores)')
|
|
61
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
62
|
+
.action(async (options: { dev?: boolean }) => {
|
|
63
|
+
try {
|
|
64
|
+
const credentials = await getStoredCredentials();
|
|
65
|
+
const api = createApiClient({
|
|
66
|
+
apiKey: credentials.apiKey,
|
|
67
|
+
workspace: credentials.workspace,
|
|
68
|
+
zone: credentials.zone,
|
|
69
|
+
dev: options.dev,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const spinner = createSpinner('Obteniendo secrets...');
|
|
73
|
+
spinner.start();
|
|
74
|
+
|
|
75
|
+
const response = await api.get('/api/worker/secrets');
|
|
76
|
+
const secrets: WorkerSecretItem[] = response.data;
|
|
77
|
+
|
|
78
|
+
spinner.stop();
|
|
79
|
+
|
|
80
|
+
if (!secrets || secrets.length === 0) {
|
|
81
|
+
logger.info('\n No hay secrets configurados');
|
|
82
|
+
logger.dim(' Agrega uno: plazbot workers secret set MI_KEY mi_valor');
|
|
83
|
+
console.log();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
logger.title(`Secrets (${secrets.length})`);
|
|
88
|
+
|
|
89
|
+
const rows = secrets.map(s => [
|
|
90
|
+
s.key,
|
|
91
|
+
theme.muted('••••••••'),
|
|
92
|
+
formatDate(s.updatedAt),
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
console.log(createTable(
|
|
96
|
+
['Key', 'Valor', 'Actualizado'],
|
|
97
|
+
rows,
|
|
98
|
+
));
|
|
99
|
+
|
|
100
|
+
console.log();
|
|
101
|
+
|
|
102
|
+
if (options.dev) {
|
|
103
|
+
logger.warning('Ambiente: desarrollo');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
} catch (error) {
|
|
107
|
+
const message = error instanceof Error ? error.message : 'Error desconocido';
|
|
108
|
+
logger.error(message);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── secret remove ───────────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
function secretRemoveCommand(): Command {
|
|
117
|
+
return new Command('remove')
|
|
118
|
+
.description('Elimina un secret del workspace')
|
|
119
|
+
.argument('<key>', 'Nombre del secret a eliminar')
|
|
120
|
+
.option('-f, --force', 'Eliminar sin confirmacion', false)
|
|
121
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
122
|
+
.action(async (key: string, options: { dev?: boolean; force?: boolean }) => {
|
|
123
|
+
try {
|
|
124
|
+
const credentials = await getStoredCredentials();
|
|
125
|
+
const api = createApiClient({
|
|
126
|
+
apiKey: credentials.apiKey,
|
|
127
|
+
workspace: credentials.workspace,
|
|
128
|
+
zone: credentials.zone,
|
|
129
|
+
dev: options.dev,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (!options.force) {
|
|
133
|
+
const confirmed = await askConfirmation(
|
|
134
|
+
` Eliminar secret "${key}"? (y/N): `
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
if (!confirmed) {
|
|
138
|
+
logger.info('\n Operacion cancelada');
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const spinner = createSpinner(`Eliminando secret "${key}"...`);
|
|
144
|
+
spinner.start();
|
|
145
|
+
|
|
146
|
+
await api.delete(`/api/worker/secrets/${encodeURIComponent(key)}`);
|
|
147
|
+
|
|
148
|
+
spinner.succeed(`Secret "${key}" eliminado`);
|
|
149
|
+
console.log();
|
|
150
|
+
|
|
151
|
+
if (options.dev) {
|
|
152
|
+
logger.warning('Ambiente: desarrollo');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
} catch (error: any) {
|
|
156
|
+
if (error.response?.status === 404) {
|
|
157
|
+
logger.error(`Secret "${key}" no encontrado`);
|
|
158
|
+
} else {
|
|
159
|
+
const message = error instanceof Error ? error.message : 'Error desconocido';
|
|
160
|
+
logger.error(message);
|
|
161
|
+
}
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function askConfirmation(question: string): Promise<boolean> {
|
|
168
|
+
return new Promise((resolve) => {
|
|
169
|
+
const rl = readline.createInterface({
|
|
170
|
+
input: process.stdin,
|
|
171
|
+
output: process.stdout,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
rl.question(question, (answer) => {
|
|
175
|
+
rl.close();
|
|
176
|
+
resolve(answer.toLowerCase() === 'y');
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getStoredCredentials } from '../../utils/credentials';
|
|
3
|
+
import { createApiClient } from '../../utils/api';
|
|
4
|
+
import { logger } from '../../utils/logger';
|
|
5
|
+
import { createSpinner, theme } from '../../utils/ui';
|
|
6
|
+
|
|
7
|
+
interface WorkerTestResult {
|
|
8
|
+
success: boolean;
|
|
9
|
+
result?: unknown;
|
|
10
|
+
error?: string;
|
|
11
|
+
duration?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const testCommand = new Command('test')
|
|
15
|
+
.description('Ejecutar un worker manualmente para probarlo')
|
|
16
|
+
.argument('<name>', 'Nombre del worker a ejecutar')
|
|
17
|
+
.option('-p, --payload <json>', 'Payload JSON para el worker (ej: \'{"producto":"iPhone"}\')')
|
|
18
|
+
.option('-t, --type <type>', 'Tipo del worker (tool, worker, sync, schedule, webhook)')
|
|
19
|
+
.option('--contact <id>', 'ID del contacto (contexto para tools)')
|
|
20
|
+
.option('--agent <id>', 'ID del agente (contexto para tools)')
|
|
21
|
+
.option('--dev', 'Usar ambiente de desarrollo', false)
|
|
22
|
+
.action(async (name: string, options: {
|
|
23
|
+
payload?: string;
|
|
24
|
+
type?: string;
|
|
25
|
+
contact?: string;
|
|
26
|
+
agent?: string;
|
|
27
|
+
dev?: boolean;
|
|
28
|
+
}) => {
|
|
29
|
+
try {
|
|
30
|
+
const credentials = await getStoredCredentials();
|
|
31
|
+
const api = createApiClient({
|
|
32
|
+
apiKey: credentials.apiKey,
|
|
33
|
+
workspace: credentials.workspace,
|
|
34
|
+
zone: credentials.zone,
|
|
35
|
+
dev: options.dev,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Parsear payload
|
|
39
|
+
let payload: Record<string, unknown> = {};
|
|
40
|
+
if (options.payload) {
|
|
41
|
+
try {
|
|
42
|
+
payload = JSON.parse(options.payload);
|
|
43
|
+
} catch {
|
|
44
|
+
logger.error('El payload no es JSON valido');
|
|
45
|
+
logger.dim('Ejemplo: --payload \'{"producto":"iPhone 15"}\'');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
logger.title(`Ejecutando worker "${name}"`);
|
|
51
|
+
console.log();
|
|
52
|
+
|
|
53
|
+
if (Object.keys(payload).length > 0) {
|
|
54
|
+
logger.label('Payload', JSON.stringify(payload, null, 2));
|
|
55
|
+
console.log();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const spinner = createSpinner('Ejecutando...');
|
|
59
|
+
spinner.start();
|
|
60
|
+
|
|
61
|
+
const response = await api.post('/api/worker/execute', {
|
|
62
|
+
workerName: name,
|
|
63
|
+
type: options.type,
|
|
64
|
+
payload,
|
|
65
|
+
triggerSource: 'cli-test',
|
|
66
|
+
agentId: options.agent,
|
|
67
|
+
contactId: options.contact,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const data = response.data as WorkerTestResult;
|
|
71
|
+
const duration = data.duration ?? 0;
|
|
72
|
+
|
|
73
|
+
spinner.stop();
|
|
74
|
+
|
|
75
|
+
if (data.success) {
|
|
76
|
+
logger.success(`Ejecutado en ${duration}ms`);
|
|
77
|
+
} else {
|
|
78
|
+
logger.error(`Fallo en ${duration}ms`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log();
|
|
82
|
+
|
|
83
|
+
// Mostrar resultado
|
|
84
|
+
if (data.result !== undefined && data.result !== null) {
|
|
85
|
+
logger.label('Resultado', '');
|
|
86
|
+
|
|
87
|
+
const result = data.result as Record<string, unknown>;
|
|
88
|
+
|
|
89
|
+
// Si es un ToolResponse, mostrar formateado
|
|
90
|
+
if (typeof result === 'object' && 'result' in result) {
|
|
91
|
+
console.log();
|
|
92
|
+
logger.label(' result', theme.success(String(result.result)));
|
|
93
|
+
|
|
94
|
+
if (result.data) {
|
|
95
|
+
logger.label(' data', JSON.stringify(result.data, null, 2));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (Array.isArray(result.actions) && result.actions.length > 0) {
|
|
99
|
+
logger.label(' actions', '');
|
|
100
|
+
for (const action of result.actions) {
|
|
101
|
+
const a = action as Record<string, string>;
|
|
102
|
+
logger.dim(` - ${a.type}: ${a.value}${a.code ? ` (${a.code})` : ''}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
// Resultado generico
|
|
107
|
+
console.log(JSON.stringify(result, null, 2));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Mostrar error si hubo
|
|
112
|
+
if (data.error) {
|
|
113
|
+
console.log();
|
|
114
|
+
logger.label('Error', theme.error(data.error));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log();
|
|
118
|
+
|
|
119
|
+
if (options.dev) {
|
|
120
|
+
logger.warning('Ambiente: desarrollo');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
} catch (error: any) {
|
|
124
|
+
if (error.response?.status === 404) {
|
|
125
|
+
logger.error(`Worker "${name}" no encontrado o inactivo`);
|
|
126
|
+
logger.dim('Verifica que el worker este desplegado con: plazbot workers list');
|
|
127
|
+
} else {
|
|
128
|
+
const message = error.response?.data?.message || error.message || 'Error desconocido';
|
|
129
|
+
logger.error(message);
|
|
130
|
+
}
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { BaseCommandOptions } from './common';
|
|
2
|
+
|
|
3
|
+
// ── Opciones de comandos ────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
export interface WorkerDeployOptions extends BaseCommandOptions {
|
|
6
|
+
file?: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface WorkerListOptions extends BaseCommandOptions {
|
|
12
|
+
type?: string;
|
|
13
|
+
status?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface WorkerLogsOptions extends BaseCommandOptions {
|
|
17
|
+
name: string;
|
|
18
|
+
limit?: number;
|
|
19
|
+
status?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface WorkerRemoveOptions extends BaseCommandOptions {
|
|
23
|
+
force?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface WorkerSecretSetOptions extends BaseCommandOptions {
|
|
27
|
+
key: string;
|
|
28
|
+
value: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface WorkerSecretListOptions extends BaseCommandOptions {}
|
|
32
|
+
|
|
33
|
+
export interface WorkerSecretRemoveOptions extends BaseCommandOptions {
|
|
34
|
+
force?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface WorkerTestOptions extends BaseCommandOptions {
|
|
38
|
+
file: string;
|
|
39
|
+
event?: string;
|
|
40
|
+
input?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── Respuestas del API ──────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
export interface WorkerListItem {
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
type: 'tool' | 'worker' | 'sync' | 'schedule' | 'webhook';
|
|
49
|
+
status: 'active' | 'inactive' | 'error';
|
|
50
|
+
description?: string;
|
|
51
|
+
cronExpression?: string;
|
|
52
|
+
webhookUrl?: string;
|
|
53
|
+
executionCount: number;
|
|
54
|
+
errorCount: number;
|
|
55
|
+
avgDurationMs: number;
|
|
56
|
+
lastExecutedAt?: string;
|
|
57
|
+
lastError?: string;
|
|
58
|
+
createdAt: string;
|
|
59
|
+
updatedAt: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface WorkerLogItem {
|
|
63
|
+
id: string;
|
|
64
|
+
workerName: string;
|
|
65
|
+
workerType: string;
|
|
66
|
+
status: 'success' | 'error' | 'timeout';
|
|
67
|
+
durationMs: number;
|
|
68
|
+
error?: string;
|
|
69
|
+
triggerSource?: string;
|
|
70
|
+
createdAt: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface WorkerSecretItem {
|
|
74
|
+
key: string;
|
|
75
|
+
createdAt: string;
|
|
76
|
+
updatedAt: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface WorkerDeployResult {
|
|
80
|
+
id: string;
|
|
81
|
+
name: string;
|
|
82
|
+
type: string;
|
|
83
|
+
status: string;
|
|
84
|
+
webhookUrl?: string;
|
|
85
|
+
cronExpression?: string;
|
|
86
|
+
}
|
package/src/utils/api.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Utilidades para llamadas directas al API de Plazbot
|
|
2
|
+
// Usado por comandos que no tienen clase SDK (ej: workers)
|
|
3
|
+
|
|
4
|
+
import axios, { AxiosInstance } from 'axios';
|
|
5
|
+
|
|
6
|
+
interface ApiClientOptions {
|
|
7
|
+
apiKey: string;
|
|
8
|
+
workspace: string;
|
|
9
|
+
zone: 'LA' | 'EU';
|
|
10
|
+
dev?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getBaseUrl(zone: string, dev?: boolean): string {
|
|
14
|
+
if (dev) return 'http://localhost:5090';
|
|
15
|
+
return zone === 'EU' ? 'https://apieu.plazbot.com' : 'https://api.plazbot.com';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createApiClient(options: ApiClientOptions): AxiosInstance {
|
|
19
|
+
const baseURL = getBaseUrl(options.zone, options.dev);
|
|
20
|
+
|
|
21
|
+
return axios.create({
|
|
22
|
+
baseURL,
|
|
23
|
+
headers: {
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
'x-api-key': options.apiKey,
|
|
26
|
+
'x-workspace-id': options.workspace,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|