plazbot-cli 0.4.1 → 0.5.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 +67 -111
- package/dist/cli.js +26 -11
- package/dist/commands/agent/ai-config.js +264 -63
- package/dist/commands/agent/chat.js +16 -11
- package/dist/commands/agent/copy.js +49 -13
- package/dist/commands/agent/create.js +62 -17
- package/dist/commands/agent/delete.js +50 -39
- package/dist/commands/agent/enable-widget.js +47 -27
- package/dist/commands/agent/export.js +180 -91
- package/dist/commands/agent/files.js +155 -73
- package/dist/commands/agent/get.js +25 -19
- package/dist/commands/agent/index.js +12 -10
- package/dist/commands/agent/list.js +20 -11
- package/dist/commands/agent/monitor.js +103 -57
- package/dist/commands/agent/on-message.js +55 -32
- package/dist/commands/agent/set.js +230 -82
- package/dist/commands/agent/templates.js +259 -156
- package/dist/commands/agent/tools.js +356 -123
- package/dist/commands/agent/update.js +69 -28
- package/dist/commands/agent/validate.js +72 -40
- package/dist/commands/agent/wizard.js +240 -329
- package/dist/commands/auth/browser-login.js +37 -31
- package/dist/commands/auth/login.js +49 -45
- package/dist/commands/auth/logout.js +20 -11
- package/dist/commands/auth/status.js +36 -12
- package/dist/commands/auth/workspace.js +61 -27
- package/dist/commands/portal/add-agent.js +106 -45
- package/dist/commands/portal/add-link.js +25 -28
- package/dist/commands/portal/clear-links.js +39 -19
- package/dist/commands/portal/create.js +66 -37
- package/dist/commands/portal/delete.js +40 -45
- package/dist/commands/portal/get.js +21 -54
- package/dist/commands/portal/index.js +12 -12
- package/dist/commands/portal/list.js +34 -52
- package/dist/commands/portal/shared.js +92 -0
- package/dist/commands/portal/update.js +100 -70
- package/dist/commands/whatsapp/broadcast.js +169 -77
- package/dist/commands/whatsapp/channels.js +167 -65
- package/dist/commands/whatsapp/chat.js +45 -46
- package/dist/commands/whatsapp/connect-flow.js +16 -15
- package/dist/commands/whatsapp/connect.js +36 -21
- package/dist/commands/whatsapp/delete-webhook.js +42 -25
- package/dist/commands/whatsapp/index.js +15 -16
- package/dist/commands/whatsapp/register-webhook.js +43 -26
- package/dist/commands/whatsapp/send-template.js +62 -61
- package/dist/commands/whatsapp/send.js +46 -33
- package/dist/commands/whatsapp/setup.js +62 -52
- package/dist/commands/whatsapp/shared.js +75 -0
- package/dist/commands/whatsapp/widget.js +82 -45
- package/dist/commands/workers/deploy.js +80 -58
- package/dist/commands/workers/index.js +8 -8
- package/dist/commands/workers/list.js +42 -20
- package/dist/commands/workers/logs.js +139 -36
- package/dist/commands/workers/remove.js +45 -47
- package/dist/commands/workers/secret.js +159 -83
- package/dist/commands/workers/test.js +108 -60
- package/dist/schemas/agent.config.schema.json +159 -8
- package/dist/studio/api/sseClient.js +1 -1
- package/dist/studio/api/studioApi.js +8 -0
- package/dist/studio/components/App.js +12 -0
- package/dist/studio/components/Footer.js +1 -1
- package/dist/studio/index.js +74 -14
- package/dist/studio/runOneShot.js +62 -23
- package/dist/studio/runRepl.js +5 -5
- package/dist/studio/slash/handlers.js +11 -8
- package/dist/studio/state/store.js +1 -1
- package/dist/utils/agent-errors.js +14 -11
- package/dist/utils/agent-patch.js +60 -0
- package/dist/utils/banner.js +9 -5
- package/dist/utils/confirm.js +31 -0
- package/dist/utils/credentials.js +13 -3
- package/dist/utils/logger.js +13 -10
- package/dist/utils/output.js +78 -0
- package/dist/utils/phone.js +22 -0
- package/dist/utils/ui.js +14 -11
- package/package.json +3 -3
|
@@ -1,17 +1,26 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
2
2
|
import { getStoredCredentials } from '../../utils/credentials.js';
|
|
3
3
|
import { createApiClient } from '../../utils/api.js';
|
|
4
4
|
import { logger } from '../../utils/logger.js';
|
|
5
|
-
import { addExamples } from '../../utils/help.js';
|
|
5
|
+
import { addExamples, addNotes } from '../../utils/help.js';
|
|
6
6
|
import { createSpinner, createTable, theme, formatDate } from '../../utils/ui.js';
|
|
7
|
+
import { jsonOption, isJsonMode, printJson, fail, CliError, describeError } from '../../utils/output.js';
|
|
8
|
+
const FOLLOW_INTERVAL_MS = 3000;
|
|
9
|
+
const FOLLOW_BATCH = 50;
|
|
7
10
|
export const logsCommand = new Command('logs')
|
|
8
|
-
.description('View execution logs
|
|
11
|
+
.description('View the execution logs of a worker')
|
|
9
12
|
.argument('<name>', 'Worker name')
|
|
10
|
-
.option('-l, --limit <number>', 'Number of logs to
|
|
11
|
-
.
|
|
13
|
+
.option('-l, --limit <number>', 'Number of logs to show, newest first (1-1000)', '20')
|
|
14
|
+
.addOption(new Option('-s, --status <status>', 'Filter by status').choices(['success', 'error', 'timeout']))
|
|
15
|
+
.option('-f, --follow', 'Keep polling every 3s and print new executions (Ctrl+C to stop)', false)
|
|
16
|
+
.addOption(jsonOption())
|
|
12
17
|
.option('--dev', 'Use development environment', false)
|
|
13
18
|
.action(async (name, options) => {
|
|
14
19
|
try {
|
|
20
|
+
const limit = Number(options.limit ?? 20);
|
|
21
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {
|
|
22
|
+
throw new CliError('--limit must be a whole number between 1 and 1000.', 'invalid_limit', 'Example: plazbot workers logs my-tool -l 100');
|
|
23
|
+
}
|
|
15
24
|
const credentials = await getStoredCredentials();
|
|
16
25
|
const api = createApiClient({
|
|
17
26
|
apiKey: credentials.apiKey,
|
|
@@ -21,62 +30,156 @@ export const logsCommand = new Command('logs')
|
|
|
21
30
|
});
|
|
22
31
|
const spinner = createSpinner(`Fetching logs for "${name}"...`);
|
|
23
32
|
spinner.start();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
let logs;
|
|
34
|
+
try {
|
|
35
|
+
logs = await fetchLogs(api, name, limit, options.status);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
spinner.stop();
|
|
39
|
+
}
|
|
40
|
+
if (options.follow) {
|
|
41
|
+
await follow(api, name, logs, options.status);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (isJsonMode()) {
|
|
45
|
+
printJson(logs.map(toJson));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (logs.length === 0) {
|
|
33
49
|
logger.info(`\n No logs found for "${name}"`);
|
|
50
|
+
logger.dim(`Check the worker name with: plazbot workers list`);
|
|
34
51
|
console.log();
|
|
35
52
|
return;
|
|
36
53
|
}
|
|
37
54
|
logger.title(`Logs for "${name}" (${logs.length})`);
|
|
38
|
-
|
|
55
|
+
console.log(createTable(['Date', 'Status', 'Duration', 'Source', 'Error'], logs.map(log => [
|
|
39
56
|
formatDate(log.createdAt),
|
|
40
|
-
log.status
|
|
41
|
-
|
|
42
|
-
: theme.warning('timeout'),
|
|
43
|
-
`${log.durationMs}ms`,
|
|
57
|
+
statusLabel(log.status),
|
|
58
|
+
`${log.durationMs ?? 0}ms`,
|
|
44
59
|
log.triggerSource || '—',
|
|
45
60
|
log.error ? theme.error(truncate(log.error, 40)) : '—',
|
|
46
|
-
]);
|
|
47
|
-
console.log(createTable(['Date', 'Status', 'Duration', 'Source', 'Error'], rows));
|
|
48
|
-
// Resumen
|
|
61
|
+
])));
|
|
49
62
|
const successCount = logs.filter(l => l.status === 'success').length;
|
|
50
63
|
const errorCount = logs.filter(l => l.status === 'error').length;
|
|
51
|
-
const
|
|
64
|
+
const timeoutCount = logs.filter(l => l.status === 'timeout').length;
|
|
65
|
+
const avgDuration = Math.round(logs.reduce((sum, l) => sum + (l.durationMs ?? 0), 0) / logs.length);
|
|
52
66
|
console.log();
|
|
53
67
|
logger.label('Successful', theme.success(`${successCount}`));
|
|
54
68
|
logger.label('Errors', errorCount > 0 ? theme.error(`${errorCount}`) : theme.muted('0'));
|
|
69
|
+
logger.label('Timeouts', timeoutCount > 0 ? theme.warning(`${timeoutCount}`) : theme.muted('0'));
|
|
55
70
|
logger.label('Average duration', `${avgDuration}ms`);
|
|
71
|
+
if (logs.some(l => l.error && l.error.length > 40)) {
|
|
72
|
+
logger.dim('Errors are truncated; use --json to see the full message.');
|
|
73
|
+
}
|
|
56
74
|
console.log();
|
|
57
75
|
if (options.dev) {
|
|
58
76
|
logger.warning('Environment: development');
|
|
59
77
|
}
|
|
60
78
|
}
|
|
61
79
|
catch (error) {
|
|
62
|
-
|
|
63
|
-
logger.error(`Worker "${name}" not found`);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
67
|
-
logger.error(message);
|
|
68
|
-
}
|
|
69
|
-
process.exit(1);
|
|
80
|
+
fail(error);
|
|
70
81
|
}
|
|
71
82
|
});
|
|
72
83
|
addExamples(logsCommand, [
|
|
73
84
|
{ description: 'Show the last 20 executions of a worker',
|
|
74
|
-
command: 'plazbot workers logs
|
|
75
|
-
{ description: '
|
|
76
|
-
command: 'plazbot workers logs
|
|
77
|
-
{ description: '
|
|
78
|
-
command: 'plazbot workers logs
|
|
85
|
+
command: 'plazbot workers logs get-stock' },
|
|
86
|
+
{ description: 'Show the last 100 failed executions',
|
|
87
|
+
command: 'plazbot workers logs get-stock -l 100 -s error' },
|
|
88
|
+
{ description: 'Follow new executions in real time',
|
|
89
|
+
command: 'plazbot workers logs get-stock --follow' },
|
|
90
|
+
{ description: 'Get logs as JSON with full error messages',
|
|
91
|
+
command: 'plazbot workers logs get-stock --json' },
|
|
92
|
+
]);
|
|
93
|
+
addNotes(logsCommand, [
|
|
94
|
+
'The table truncates errors to 40 characters; --json includes the full message.',
|
|
95
|
+
'With --follow and --json, each execution is printed as one JSON object per line (NDJSON).',
|
|
79
96
|
]);
|
|
97
|
+
async function fetchLogs(api, name, limit, status) {
|
|
98
|
+
const params = { limit: String(limit) };
|
|
99
|
+
if (status)
|
|
100
|
+
params.status = status;
|
|
101
|
+
const response = await api.get(`/api/worker/${encodeURIComponent(name)}/logs`, { params });
|
|
102
|
+
return Array.isArray(response.data) ? response.data : [];
|
|
103
|
+
}
|
|
104
|
+
// Polls the logs endpoint (ordered by created_at DESC) and prints entries not seen yet.
|
|
105
|
+
async function follow(api, name, initial, status) {
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
const emit = (entries) => {
|
|
108
|
+
// API returns newest first; print oldest first
|
|
109
|
+
for (const log of [...entries].reverse()) {
|
|
110
|
+
const key = logKey(log);
|
|
111
|
+
if (seen.has(key))
|
|
112
|
+
continue;
|
|
113
|
+
seen.add(key);
|
|
114
|
+
if (isJsonMode()) {
|
|
115
|
+
process.stdout.write(JSON.stringify(toJson(log)) + '\n');
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log(formatLine(log));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
if (!isJsonMode()) {
|
|
123
|
+
logger.title(`Following logs for "${name}"`);
|
|
124
|
+
logger.dim('Press Ctrl+C to stop.');
|
|
125
|
+
console.log();
|
|
126
|
+
}
|
|
127
|
+
emit(initial);
|
|
128
|
+
process.on('SIGINT', () => {
|
|
129
|
+
if (!isJsonMode())
|
|
130
|
+
console.log();
|
|
131
|
+
process.exit(0);
|
|
132
|
+
});
|
|
133
|
+
let failing = false;
|
|
134
|
+
for (;;) {
|
|
135
|
+
await new Promise(resolve => setTimeout(resolve, FOLLOW_INTERVAL_MS));
|
|
136
|
+
try {
|
|
137
|
+
emit(await fetchLogs(api, name, FOLLOW_BATCH, status));
|
|
138
|
+
if (failing)
|
|
139
|
+
logger.dim('Connection restored.');
|
|
140
|
+
failing = false;
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
const info = describeError(error);
|
|
144
|
+
if (info.status === 401 || info.status === 403 || info.status === 404)
|
|
145
|
+
fail(error);
|
|
146
|
+
if (!failing)
|
|
147
|
+
logger.warning(`Could not fetch logs (${info.message}). Retrying...`);
|
|
148
|
+
failing = true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function logKey(log) {
|
|
153
|
+
return log.id != null ? String(log.id) : `${log.createdAt}|${log.status}|${log.durationMs}`;
|
|
154
|
+
}
|
|
155
|
+
function toJson(log) {
|
|
156
|
+
return {
|
|
157
|
+
id: log.id,
|
|
158
|
+
workerName: log.workerName,
|
|
159
|
+
workerType: log.workerType,
|
|
160
|
+
status: log.status,
|
|
161
|
+
durationMs: log.durationMs ?? 0,
|
|
162
|
+
triggerSource: log.triggerSource ?? null,
|
|
163
|
+
error: log.error ?? null,
|
|
164
|
+
createdAt: log.createdAt,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function formatLine(log) {
|
|
168
|
+
const parts = [
|
|
169
|
+
theme.muted(formatDate(log.createdAt)),
|
|
170
|
+
statusLabel(log.status),
|
|
171
|
+
`${log.durationMs ?? 0}ms`,
|
|
172
|
+
theme.muted(log.triggerSource || '—'),
|
|
173
|
+
];
|
|
174
|
+
if (log.error)
|
|
175
|
+
parts.push(theme.error(log.error));
|
|
176
|
+
return ' ' + parts.join(' ');
|
|
177
|
+
}
|
|
178
|
+
function statusLabel(status) {
|
|
179
|
+
return status === 'success' ? theme.success('ok')
|
|
180
|
+
: status === 'error' ? theme.error('error')
|
|
181
|
+
: theme.warning(status || 'timeout');
|
|
182
|
+
}
|
|
80
183
|
function truncate(text, max) {
|
|
81
184
|
if (text.length <= max)
|
|
82
185
|
return text;
|
|
@@ -4,13 +4,15 @@ import { createApiClient } from '../../utils/api.js';
|
|
|
4
4
|
import { logger } from '../../utils/logger.js';
|
|
5
5
|
import { addExamples } from '../../utils/help.js';
|
|
6
6
|
import { createSpinner } from '../../utils/ui.js';
|
|
7
|
-
import
|
|
7
|
+
import { jsonOption, isJsonMode, printJson, fail, CliError } from '../../utils/output.js';
|
|
8
|
+
import { addYesOption, confirmAction } from '../../utils/confirm.js';
|
|
8
9
|
export const removeCommand = new Command('remove')
|
|
9
10
|
.description('Delete a worker from the workspace')
|
|
10
11
|
.argument('<name>', 'Name of the worker to delete')
|
|
11
|
-
.
|
|
12
|
-
.option('--dev', 'Use development environment', false)
|
|
13
|
-
|
|
12
|
+
.addOption(jsonOption())
|
|
13
|
+
.option('--dev', 'Use development environment', false);
|
|
14
|
+
addYesOption(removeCommand);
|
|
15
|
+
removeCommand.action(async (name, options) => {
|
|
14
16
|
try {
|
|
15
17
|
const credentials = await getStoredCredentials();
|
|
16
18
|
const api = createApiClient({
|
|
@@ -19,71 +21,67 @@ export const removeCommand = new Command('remove')
|
|
|
19
21
|
zone: credentials.zone,
|
|
20
22
|
dev: options.dev,
|
|
21
23
|
});
|
|
22
|
-
// Verificar que el worker existe
|
|
23
24
|
const spinner = createSpinner(`Looking up worker "${name}"...`);
|
|
24
25
|
spinner.start();
|
|
25
26
|
let workerInfo;
|
|
26
27
|
try {
|
|
27
28
|
const response = await api.get(`/api/worker/${encodeURIComponent(name)}`);
|
|
28
29
|
workerInfo = response.data;
|
|
29
|
-
spinner.stop();
|
|
30
30
|
}
|
|
31
31
|
catch (error) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
logger.error(`Worker "${name}" not found`);
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
logger.error(error.response?.data?.message || error.message);
|
|
32
|
+
if (error?.response?.status === 404) {
|
|
33
|
+
throw new CliError(`Worker "${name}" not found.`, 'not_found', 'List your workers with: plazbot workers list');
|
|
38
34
|
}
|
|
39
|
-
|
|
35
|
+
throw error;
|
|
40
36
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
37
|
+
finally {
|
|
38
|
+
spinner.stop();
|
|
39
|
+
}
|
|
40
|
+
if (!isJsonMode()) {
|
|
41
|
+
logger.warning('You are about to delete this worker:');
|
|
42
|
+
logger.divider();
|
|
43
|
+
logger.label('Name', workerInfo?.name ?? name);
|
|
44
|
+
logger.label('Type', workerInfo?.type ?? '—');
|
|
45
|
+
logger.label('Status', workerInfo?.status ?? '—');
|
|
46
|
+
logger.label('Executions', `${workerInfo?.executionCount ?? 0}`);
|
|
47
|
+
logger.divider();
|
|
48
|
+
}
|
|
49
|
+
const ok = await confirmAction(`Delete worker "${name}"? This cannot be undone.`, options);
|
|
50
|
+
if (!ok) {
|
|
51
|
+
logger.dim('Aborted.');
|
|
52
|
+
process.exit(1);
|
|
56
53
|
}
|
|
57
54
|
const deleteSpinner = createSpinner(`Deleting "${name}"...`);
|
|
58
55
|
deleteSpinner.start();
|
|
59
|
-
|
|
56
|
+
try {
|
|
57
|
+
const response = await api.delete(`/api/worker/${encodeURIComponent(name)}`);
|
|
58
|
+
if (response.data?.success === false) {
|
|
59
|
+
throw new CliError(response.data.message || `Could not delete worker "${name}".`, 'delete_failed');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
deleteSpinner.stop();
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
60
66
|
deleteSpinner.succeed(`Worker "${name}" deleted`);
|
|
67
|
+
if (isJsonMode()) {
|
|
68
|
+
printJson({ name, removed: true });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
61
71
|
console.log();
|
|
62
72
|
if (options.dev) {
|
|
63
73
|
logger.warning('Environment: development');
|
|
64
74
|
}
|
|
65
75
|
}
|
|
66
76
|
catch (error) {
|
|
67
|
-
|
|
68
|
-
logger.error(message);
|
|
69
|
-
process.exit(1);
|
|
77
|
+
fail(error);
|
|
70
78
|
}
|
|
71
79
|
});
|
|
72
80
|
addExamples(removeCommand, [
|
|
73
|
-
{ description: 'Delete a worker
|
|
74
|
-
command: 'plazbot workers remove
|
|
81
|
+
{ description: 'Delete a worker (asks for confirmation)',
|
|
82
|
+
command: 'plazbot workers remove get-stock' },
|
|
75
83
|
{ description: 'Delete without confirmation (scripts/CI)',
|
|
76
|
-
command: 'plazbot workers remove
|
|
84
|
+
command: 'plazbot workers remove get-stock --yes' },
|
|
85
|
+
{ description: 'Delete and get the result as JSON',
|
|
86
|
+
command: 'plazbot workers remove get-stock --yes --json' },
|
|
77
87
|
]);
|
|
78
|
-
function askConfirmation(question) {
|
|
79
|
-
return new Promise((resolve) => {
|
|
80
|
-
const rl = readline.createInterface({
|
|
81
|
-
input: process.stdin,
|
|
82
|
-
output: process.stdout,
|
|
83
|
-
});
|
|
84
|
-
rl.question(question, (answer) => {
|
|
85
|
-
rl.close();
|
|
86
|
-
resolve(answer.toLowerCase() === 'y');
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
}
|