plazbot-cli 0.4.1 → 0.5.0
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 +179 -88
- package/dist/commands/agent/files.js +155 -73
- package/dist/commands/agent/get.js +24 -16
- package/dist/commands/agent/index.js +12 -10
- package/dist/commands/agent/list.js +20 -11
- package/dist/commands/agent/monitor.js +101 -53
- 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 +77 -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 +2 -2
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
2
2
|
import { Agent } from 'plazbot';
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
4
|
import { getStoredCredentials } from '../../utils/credentials.js';
|
|
5
5
|
import { logger } from '../../utils/logger.js';
|
|
6
|
-
import { addExamples } from '../../utils/help.js';
|
|
6
|
+
import { addExamples, addNotes } from '../../utils/help.js';
|
|
7
7
|
import { describeAgentLoadError } from '../../utils/agent-errors.js';
|
|
8
8
|
import { createSpinner, theme, section, kvPair } from '../../utils/ui.js';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
9
|
+
import { jsonOption, isJsonMode, printJson, fail, CliError } from '../../utils/output.js';
|
|
10
|
+
import { requireInteractive } from '../../utils/confirm.js';
|
|
11
|
+
export const AI_PROVIDERS = [
|
|
12
|
+
{ value: 'openai', label: 'OpenAI' },
|
|
13
|
+
{ value: 'claude', label: 'Anthropic (Claude)' },
|
|
14
|
+
{ value: 'gemini', label: 'Google (Gemini)' },
|
|
15
|
+
];
|
|
16
|
+
export const AI_MODELS = {
|
|
12
17
|
openai: [
|
|
13
|
-
// GPT-5.4 (flagship
|
|
18
|
+
// GPT-5.4 (current flagship)
|
|
14
19
|
'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.4-nano',
|
|
15
20
|
// GPT-5.2
|
|
16
21
|
'gpt-5.2',
|
|
@@ -22,7 +27,7 @@ const MODELS = {
|
|
|
22
27
|
'gpt-4.1', 'gpt-4.1-mini', 'gpt-4.1-nano',
|
|
23
28
|
// GPT-4o
|
|
24
29
|
'gpt-4o', 'gpt-4o-mini',
|
|
25
|
-
//
|
|
30
|
+
// Reasoning (o-series)
|
|
26
31
|
'o4-mini', 'o3', 'o3-mini', 'o1-2024-12-17',
|
|
27
32
|
// Legacy
|
|
28
33
|
'gpt-4-turbo', 'gpt-3.5-turbo',
|
|
@@ -38,7 +43,7 @@ const MODELS = {
|
|
|
38
43
|
'claude-opus-4-1-20250805', 'claude-opus-4-20250514', 'claude-sonnet-4-20250514',
|
|
39
44
|
],
|
|
40
45
|
gemini: [
|
|
41
|
-
// Gemini 3.1 (flagship
|
|
46
|
+
// Gemini 3.1 (current flagship)
|
|
42
47
|
'gemini-3.1-pro-preview', 'gemini-3.1-flash-lite-preview',
|
|
43
48
|
// Gemini 2.5
|
|
44
49
|
'gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.5-flash-lite',
|
|
@@ -46,17 +51,135 @@ const MODELS = {
|
|
|
46
51
|
'gemini-2.0-flash',
|
|
47
52
|
],
|
|
48
53
|
};
|
|
49
|
-
const
|
|
54
|
+
export const DEFAULT_AI_MODEL = {
|
|
50
55
|
openai: 'gpt-4o',
|
|
51
56
|
claude: 'claude-sonnet-4-6',
|
|
52
57
|
gemini: 'gemini-2.5-pro',
|
|
53
58
|
};
|
|
59
|
+
export const DEFAULT_TEMPERATURE = 0.7;
|
|
60
|
+
export const DEFAULT_MAX_TOKENS = 4096;
|
|
61
|
+
export const MAX_TOKENS_LIMIT = 128000;
|
|
62
|
+
const PROVIDER_ALIASES = {
|
|
63
|
+
openai: 'openai',
|
|
64
|
+
gpt: 'openai',
|
|
65
|
+
claude: 'claude',
|
|
66
|
+
anthropic: 'claude',
|
|
67
|
+
gemini: 'gemini',
|
|
68
|
+
google: 'gemini',
|
|
69
|
+
};
|
|
70
|
+
/** Maps user input (openai, anthropic, claude, google, gemini) to the API provider value. */
|
|
71
|
+
export function normalizeProvider(value) {
|
|
72
|
+
const provider = PROVIDER_ALIASES[String(value || '').trim().toLowerCase()];
|
|
73
|
+
if (!provider) {
|
|
74
|
+
throw new CliError(`Unknown AI provider "${value}".`, 'invalid_provider', 'Use one of: openai, anthropic (claude), google (gemini).');
|
|
75
|
+
}
|
|
76
|
+
return provider;
|
|
77
|
+
}
|
|
78
|
+
/** Validates a temperature (0-2). */
|
|
79
|
+
export function parseTemperature(value) {
|
|
80
|
+
const raw = String(value ?? '').trim();
|
|
81
|
+
const n = Number(raw);
|
|
82
|
+
if (raw === '' || !Number.isFinite(n) || n < 0 || n > 2) {
|
|
83
|
+
throw new CliError(`Invalid temperature "${value}".`, 'invalid_temperature', 'Use a number between 0 and 2 (e.g. 0.7).');
|
|
84
|
+
}
|
|
85
|
+
return n;
|
|
86
|
+
}
|
|
87
|
+
/** Validates max tokens (integer, 1-128000). */
|
|
88
|
+
export function parseMaxTokens(value) {
|
|
89
|
+
const raw = String(value ?? '').trim();
|
|
90
|
+
const n = Number(raw);
|
|
91
|
+
if (raw === '' || !Number.isInteger(n) || n < 1 || n > MAX_TOKENS_LIMIT) {
|
|
92
|
+
throw new CliError(`Invalid max tokens "${value}".`, 'invalid_max_tokens', `Use a whole number between 1 and ${MAX_TOKENS_LIMIT} (e.g. 4096).`);
|
|
93
|
+
}
|
|
94
|
+
return n;
|
|
95
|
+
}
|
|
96
|
+
/** Masks a secret so it is never printed in full: "sk-p…a1B2". */
|
|
97
|
+
export function maskToken(token) {
|
|
98
|
+
if (!token)
|
|
99
|
+
return '';
|
|
100
|
+
const t = String(token);
|
|
101
|
+
if (t.length <= 10)
|
|
102
|
+
return '••••';
|
|
103
|
+
return `${t.slice(0, 4)}…${t.slice(-4)}`;
|
|
104
|
+
}
|
|
105
|
+
/** Inquirer validator built from a parser that throws CliError. */
|
|
106
|
+
export function validateWith(parser) {
|
|
107
|
+
return (v) => {
|
|
108
|
+
try {
|
|
109
|
+
parser(v);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
return e instanceof CliError ? `${e.message} ${e.hint || ''}`.trim() : 'Invalid value';
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function warnIfUnknownModel(provider, model) {
|
|
118
|
+
if (!AI_MODELS[provider].includes(model)) {
|
|
119
|
+
logger.warning(`Model "${model}" is not in the known ${provider} list. It will be saved as is; make sure the provider accepts it.`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function publicProviders(providers) {
|
|
123
|
+
return (providers || []).map((p) => ({
|
|
124
|
+
provider: p.provider,
|
|
125
|
+
model: p.model,
|
|
126
|
+
apiToken: maskToken(p.apiToken),
|
|
127
|
+
temperature: p.temperature,
|
|
128
|
+
maxTokens: p.maxTokens,
|
|
129
|
+
isDefault: !!p.isDefault,
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
function printCurrentConfig(agentData, agentId) {
|
|
133
|
+
console.log(section('AI configuration - ' + (agentData.name || agentId)));
|
|
134
|
+
const providers = agentData.aiProviders || [];
|
|
135
|
+
if (providers.length > 0 && agentData.customAIConfig) {
|
|
136
|
+
providers.forEach((p) => {
|
|
137
|
+
console.log(kvPair(' Provider', p.provider));
|
|
138
|
+
console.log(kvPair(' Model', p.model));
|
|
139
|
+
console.log(kvPair(' API token', p.apiToken ? maskToken(p.apiToken) : theme.warning('not set')));
|
|
140
|
+
console.log(kvPair(' Temperature', String(p.temperature ?? DEFAULT_TEMPERATURE)));
|
|
141
|
+
console.log(kvPair(' Max tokens', String(p.maxTokens ?? '-')));
|
|
142
|
+
console.log(kvPair(' Default', p.isDefault ? 'Yes' : 'No'));
|
|
143
|
+
console.log();
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log(theme.muted('\n Using the platform default AI (Plazbot)\n'));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function stripIds(data) {
|
|
151
|
+
const { id: _id1, _id: _id2, ...rest } = data;
|
|
152
|
+
return rest;
|
|
153
|
+
}
|
|
54
154
|
export const aiConfigCommand = new Command('ai-config')
|
|
55
|
-
.description(
|
|
56
|
-
.argument('<agentId>', 'Agent ID')
|
|
155
|
+
.description("Show or change the agent's AI provider, model and API token")
|
|
156
|
+
.argument('<agentId>', 'Agent ID (e.g. age_AbcDef123)')
|
|
157
|
+
.option('--show', 'Print the current AI configuration and exit (tokens are masked)')
|
|
158
|
+
.addOption(new Option('--provider <provider>', 'AI provider: openai, anthropic (claude) or google (gemini)'))
|
|
159
|
+
.option('--model <id>', 'Model ID (default depends on the provider, e.g. gpt-4o, claude-sonnet-4-6)')
|
|
160
|
+
.option('--api-token <token>', "Provider API key (kept from the current config when the provider doesn't change)")
|
|
161
|
+
.option('--temperature <0-2>', `Sampling temperature between 0 and 2 (default: ${DEFAULT_TEMPERATURE})`)
|
|
162
|
+
.option('--max-tokens <n>', `Maximum tokens per response, 1-${MAX_TOKENS_LIMIT} (default: ${DEFAULT_MAX_TOKENS})`)
|
|
163
|
+
.option('--default', 'Drop the custom provider and use the platform default AI')
|
|
164
|
+
.addOption(jsonOption())
|
|
57
165
|
.option('--dev', 'Use development environment', false)
|
|
58
166
|
.action(async (agentId, options) => {
|
|
59
167
|
try {
|
|
168
|
+
const hasSetFlags = !!(options.provider || options.model || options.apiToken || options.temperature !== undefined || options.maxTokens !== undefined);
|
|
169
|
+
if (options.default && hasSetFlags) {
|
|
170
|
+
throw new CliError('--default cannot be combined with --provider, --model, --api-token, --temperature or --max-tokens.', 'conflicting_flags');
|
|
171
|
+
}
|
|
172
|
+
if (options.show && (hasSetFlags || options.default)) {
|
|
173
|
+
throw new CliError('--show cannot be combined with flags that change the configuration.', 'conflicting_flags');
|
|
174
|
+
}
|
|
175
|
+
// Validate flag values before any API call
|
|
176
|
+
const flagProvider = options.provider ? normalizeProvider(options.provider) : undefined;
|
|
177
|
+
const flagTemperature = options.temperature !== undefined ? parseTemperature(options.temperature) : undefined;
|
|
178
|
+
const flagMaxTokens = options.maxTokens !== undefined ? parseMaxTokens(options.maxTokens) : undefined;
|
|
179
|
+
const interactive = !options.show && !options.default && !hasSetFlags;
|
|
180
|
+
if (interactive) {
|
|
181
|
+
requireInteractive('The AI configuration menu', `Pass flags instead, e.g. plazbot agent ai-config ${agentId} --provider openai --model gpt-4o --api-token sk-..., or use --show / --default.`);
|
|
182
|
+
}
|
|
60
183
|
const credentials = await getStoredCredentials();
|
|
61
184
|
const agent = new Agent({
|
|
62
185
|
workspaceId: credentials.workspace,
|
|
@@ -74,79 +197,157 @@ export const aiConfigCommand = new Command('ai-config')
|
|
|
74
197
|
}
|
|
75
198
|
catch (loadErr) {
|
|
76
199
|
spinner.stop();
|
|
77
|
-
|
|
78
|
-
process.exit(1);
|
|
200
|
+
throw new CliError(describeAgentLoadError(loadErr, agentId, credentials, { dev: options.dev }), 'agent_not_found');
|
|
79
201
|
}
|
|
80
|
-
console.log(section('AI Configuration - ' + (agentData.name || agentId)));
|
|
81
202
|
const currentProviders = agentData.aiProviders || [];
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
203
|
+
const current = agentData.customAIConfig
|
|
204
|
+
? (currentProviders.find((p) => p.isDefault) || currentProviders[0])
|
|
205
|
+
: undefined;
|
|
206
|
+
const saveDefault = async () => {
|
|
207
|
+
const s = createSpinner('Switching to the platform default AI...');
|
|
208
|
+
s.start();
|
|
209
|
+
await agent.updateAgent(agentId, stripIds({ ...agentData, customAIConfig: false, aiProviders: [] }));
|
|
210
|
+
s.succeed('Using the platform default AI (Plazbot)');
|
|
211
|
+
if (isJsonMode())
|
|
212
|
+
printJson({ id: agentId, provider: 'default', model: null, updated: true });
|
|
213
|
+
};
|
|
214
|
+
const saveProvider = async (p) => {
|
|
215
|
+
const s = createSpinner('Saving AI configuration...');
|
|
216
|
+
s.start();
|
|
217
|
+
await agent.updateAgent(agentId, stripIds({
|
|
218
|
+
...agentData,
|
|
219
|
+
customAIConfig: true,
|
|
220
|
+
aiProviders: [{ ...p, isDefault: true }],
|
|
221
|
+
}));
|
|
222
|
+
s.succeed(`AI provider set: ${p.provider} / ${p.model} (token ${maskToken(p.apiToken)})`);
|
|
223
|
+
if (isJsonMode())
|
|
224
|
+
printJson({ id: agentId, provider: p.provider, model: p.model, updated: true });
|
|
225
|
+
};
|
|
226
|
+
// --show
|
|
227
|
+
if (options.show) {
|
|
228
|
+
if (isJsonMode()) {
|
|
229
|
+
printJson({
|
|
230
|
+
id: agentId,
|
|
231
|
+
name: agentData.name,
|
|
232
|
+
customAIConfig: !!agentData.customAIConfig,
|
|
233
|
+
providers: agentData.customAIConfig ? publicProviders(currentProviders) : [],
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
printCurrentConfig(agentData, agentId);
|
|
238
|
+
}
|
|
239
|
+
return;
|
|
91
240
|
}
|
|
92
|
-
|
|
93
|
-
|
|
241
|
+
// --default
|
|
242
|
+
if (options.default) {
|
|
243
|
+
await saveDefault();
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
// Non-interactive set via flags
|
|
247
|
+
if (hasSetFlags) {
|
|
248
|
+
const provider = flagProvider || current?.provider;
|
|
249
|
+
if (!provider) {
|
|
250
|
+
throw new CliError('This agent uses the platform default AI, so --provider is required.', 'missing_provider', 'Pass --provider openai|anthropic|google and --api-token <token>.');
|
|
251
|
+
}
|
|
252
|
+
const sameProvider = !!current && current.provider === provider;
|
|
253
|
+
const apiToken = options.apiToken || (sameProvider ? current.apiToken : '');
|
|
254
|
+
if (!apiToken) {
|
|
255
|
+
throw new CliError(`An API token is required for ${provider}.`, 'missing_api_token', 'Pass it with --api-token <token>.');
|
|
256
|
+
}
|
|
257
|
+
const model = options.model || (sameProvider && current.model) || DEFAULT_AI_MODEL[provider];
|
|
258
|
+
warnIfUnknownModel(provider, model);
|
|
259
|
+
await saveProvider({
|
|
260
|
+
provider,
|
|
261
|
+
model,
|
|
262
|
+
apiToken,
|
|
263
|
+
temperature: flagTemperature ?? (sameProvider ? current.temperature : undefined) ?? DEFAULT_TEMPERATURE,
|
|
264
|
+
maxTokens: flagMaxTokens ?? (sameProvider ? current.maxTokens : undefined) ?? DEFAULT_MAX_TOKENS,
|
|
265
|
+
});
|
|
266
|
+
return;
|
|
94
267
|
}
|
|
268
|
+
// Interactive
|
|
269
|
+
printCurrentConfig(agentData, agentId);
|
|
95
270
|
const { action } = await inquirer.prompt([{
|
|
96
271
|
type: 'list',
|
|
97
272
|
name: 'action',
|
|
98
273
|
message: 'What would you like to do?',
|
|
99
274
|
choices: [
|
|
100
|
-
{ name: '
|
|
101
|
-
{ name: 'Use default
|
|
275
|
+
{ name: 'Set a custom AI provider', value: 'set' },
|
|
276
|
+
{ name: 'Use the platform default AI', value: 'reset' },
|
|
102
277
|
{ name: 'Exit', value: 'exit' },
|
|
103
278
|
],
|
|
104
279
|
}]);
|
|
105
280
|
if (action === 'exit')
|
|
106
281
|
return;
|
|
107
282
|
if (action === 'reset') {
|
|
108
|
-
|
|
109
|
-
const resetSpinner = createSpinner('Resetting configuration...');
|
|
110
|
-
resetSpinner.start();
|
|
111
|
-
await agent.updateAgent(agentId, resetConfig);
|
|
112
|
-
resetSpinner.succeed('Using default configuration');
|
|
283
|
+
await saveDefault();
|
|
113
284
|
return;
|
|
114
285
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
286
|
+
const { provider } = await inquirer.prompt([{
|
|
287
|
+
type: 'list',
|
|
288
|
+
name: 'provider',
|
|
289
|
+
message: 'Provider:',
|
|
290
|
+
choices: AI_PROVIDERS.map(p => ({ name: p.label, value: p.value })),
|
|
291
|
+
default: current?.provider || 'openai',
|
|
292
|
+
}]);
|
|
293
|
+
const providerId = provider;
|
|
294
|
+
const sameProvider = !!current && current.provider === providerId;
|
|
295
|
+
const keepToken = sameProvider && !!current.apiToken;
|
|
296
|
+
const answers = await inquirer.prompt([
|
|
297
|
+
{
|
|
298
|
+
type: 'list',
|
|
299
|
+
name: 'model',
|
|
300
|
+
message: 'Model:',
|
|
301
|
+
choices: AI_MODELS[providerId],
|
|
302
|
+
default: (sameProvider && AI_MODELS[providerId].includes(current.model)) ? current.model : DEFAULT_AI_MODEL[providerId],
|
|
303
|
+
pageSize: 12,
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
type: 'password',
|
|
307
|
+
name: 'apiToken',
|
|
308
|
+
message: keepToken ? `API token (leave empty to keep ${maskToken(current.apiToken)}):` : 'API token:',
|
|
309
|
+
mask: '*',
|
|
310
|
+
validate: (v) => (v.trim().length > 0 || keepToken) ? true : 'The API token is required',
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
type: 'input',
|
|
314
|
+
name: 'temperature',
|
|
315
|
+
message: 'Temperature (0-2):',
|
|
316
|
+
default: String((sameProvider ? current.temperature : undefined) ?? DEFAULT_TEMPERATURE),
|
|
317
|
+
validate: validateWith(parseTemperature),
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
type: 'input',
|
|
321
|
+
name: 'maxTokens',
|
|
322
|
+
message: `Max tokens per response (1-${MAX_TOKENS_LIMIT}):`,
|
|
323
|
+
default: String((sameProvider ? current.maxTokens : undefined) || DEFAULT_MAX_TOKENS),
|
|
324
|
+
validate: validateWith(parseMaxTokens),
|
|
325
|
+
},
|
|
125
326
|
]);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
temperature: config.temperature,
|
|
134
|
-
maxTokens: config.maxTokens,
|
|
135
|
-
isDefault: true,
|
|
136
|
-
}],
|
|
137
|
-
};
|
|
138
|
-
const updateSpinner = createSpinner('Saving configuration...');
|
|
139
|
-
updateSpinner.start();
|
|
140
|
-
await agent.updateAgent(agentId, updatedConfig);
|
|
141
|
-
updateSpinner.succeed(`Provider configured: ${ai.provider} / ${config.model}`);
|
|
327
|
+
await saveProvider({
|
|
328
|
+
provider: providerId,
|
|
329
|
+
model: answers.model,
|
|
330
|
+
apiToken: answers.apiToken.trim() || current.apiToken,
|
|
331
|
+
temperature: parseTemperature(answers.temperature),
|
|
332
|
+
maxTokens: parseMaxTokens(answers.maxTokens),
|
|
333
|
+
});
|
|
142
334
|
}
|
|
143
335
|
catch (error) {
|
|
144
|
-
|
|
145
|
-
logger.error(message);
|
|
146
|
-
process.exit(1);
|
|
336
|
+
fail(error);
|
|
147
337
|
}
|
|
148
338
|
});
|
|
149
339
|
addExamples(aiConfigCommand, [
|
|
150
|
-
{ description: 'Pick provider
|
|
151
|
-
command: 'plazbot agent ai-config
|
|
340
|
+
{ description: 'Pick provider, model and API token interactively',
|
|
341
|
+
command: 'plazbot agent ai-config age_AbcDef123' },
|
|
342
|
+
{ description: 'Show the current AI configuration as JSON (tokens masked)',
|
|
343
|
+
command: 'plazbot agent ai-config age_AbcDef123 --show --json' },
|
|
344
|
+
{ description: 'Switch to Claude non-interactively',
|
|
345
|
+
command: 'plazbot agent ai-config age_AbcDef123 --provider anthropic --model claude-sonnet-4-6 --api-token sk-ant-xxx --temperature 0.5' },
|
|
346
|
+
{ description: 'Go back to the platform default AI',
|
|
347
|
+
command: 'plazbot agent ai-config age_AbcDef123 --default' },
|
|
348
|
+
]);
|
|
349
|
+
addNotes(aiConfigCommand, [
|
|
350
|
+
'Changing only --model, --temperature or --max-tokens keeps the current provider and API token.',
|
|
351
|
+
`Known models: openai (${AI_MODELS.openai.length}), claude (${AI_MODELS.claude.length}), gemini (${AI_MODELS.gemini.length}). Unknown model IDs are accepted with a warning.`,
|
|
352
|
+
'API tokens are never printed in full.',
|
|
152
353
|
]);
|
|
@@ -2,9 +2,11 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { Agent } from 'plazbot';
|
|
3
3
|
import { getStoredCredentials } from '../../utils/credentials.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 { describeAgentLoadError } from '../../utils/agent-errors.js';
|
|
7
7
|
import { section } from '../../utils/ui.js';
|
|
8
|
+
import { fail, describeError } from '../../utils/output.js';
|
|
9
|
+
import { requireInteractive } from '../../utils/confirm.js';
|
|
8
10
|
import crypto from 'crypto';
|
|
9
11
|
import readline from 'readline';
|
|
10
12
|
import chalk from 'chalk';
|
|
@@ -19,11 +21,12 @@ const COMMANDS_HELP = `
|
|
|
19
21
|
export const chatCommand = new Command('chat')
|
|
20
22
|
.description('Start an interactive chat session with an agent')
|
|
21
23
|
.argument('<agentId>', 'Agent ID')
|
|
22
|
-
.option('-s, --session-id <id>', '
|
|
23
|
-
.option('-m, --multiple-answers', 'Allow
|
|
24
|
+
.option('-s, --session-id <id>', 'Reuse a session ID to continue a conversation (default: a new random ID)')
|
|
25
|
+
.option('-m, --multiple-answers', 'Allow the agent to send several answers per turn', false)
|
|
24
26
|
.option('--dev', 'Use development environment', false)
|
|
25
27
|
.action(async (agentId, options) => {
|
|
26
28
|
try {
|
|
29
|
+
requireInteractive('The interactive chat', `For scripts, send one message at a time: plazbot agent message ${agentId} -q "Hello" --json`);
|
|
27
30
|
const credentials = await getStoredCredentials();
|
|
28
31
|
const agent = new Agent({
|
|
29
32
|
workspaceId: credentials.workspace,
|
|
@@ -143,8 +146,9 @@ export const chatCommand = new Command('chat')
|
|
|
143
146
|
askQuestion();
|
|
144
147
|
}
|
|
145
148
|
catch (error) {
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
// Error de un turno: se informa y la sesion sigue abierta
|
|
150
|
+
const { message, hint } = describeError(error);
|
|
151
|
+
console.log(chalk.hex('#EF5350')(`\n Error: ${message}`) + (hint ? chalk.gray(`\n ${hint}`) : '') + '\n');
|
|
148
152
|
askQuestion();
|
|
149
153
|
}
|
|
150
154
|
});
|
|
@@ -152,18 +156,19 @@ export const chatCommand = new Command('chat')
|
|
|
152
156
|
askQuestion();
|
|
153
157
|
}
|
|
154
158
|
catch (error) {
|
|
155
|
-
|
|
156
|
-
logger.error(message);
|
|
157
|
-
process.exit(1);
|
|
159
|
+
fail(error);
|
|
158
160
|
}
|
|
159
161
|
});
|
|
160
162
|
addExamples(chatCommand, [
|
|
161
163
|
{ description: 'Open an interactive chat with the agent',
|
|
162
|
-
command: 'plazbot agent chat
|
|
164
|
+
command: 'plazbot agent chat age_AbcDef123' },
|
|
163
165
|
{ description: 'Reuse a previous session ID to continue the conversation',
|
|
164
|
-
command: 'plazbot agent chat
|
|
166
|
+
command: 'plazbot agent chat age_AbcDef123 -s sess_AbcDef123' },
|
|
165
167
|
{ description: 'Allow the agent to send multiple answers per turn',
|
|
166
|
-
command: 'plazbot agent chat
|
|
168
|
+
command: 'plazbot agent chat age_AbcDef123 -m' },
|
|
169
|
+
]);
|
|
170
|
+
addNotes(chatCommand, [
|
|
171
|
+
'Needs an interactive terminal. For scripts use: plazbot agent message <agentId> -q "..." --json',
|
|
167
172
|
]);
|
|
168
173
|
function formatResponse(text) {
|
|
169
174
|
if (!text)
|
|
@@ -2,11 +2,13 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { Agent } from 'plazbot';
|
|
3
3
|
import { getStoredCredentials } from '../../utils/credentials.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 } from '../../utils/ui.js';
|
|
7
|
+
import { jsonOption, isJsonMode, printJson, fail, CliError } from '../../utils/output.js';
|
|
7
8
|
export const copyCommand = new Command('copy')
|
|
8
|
-
.description('Clone an
|
|
9
|
+
.description('Clone an agent inside the active workspace')
|
|
9
10
|
.argument('<agentId>', 'Agent ID to clone')
|
|
11
|
+
.addOption(jsonOption('Print the result as JSON ({"sourceId","id","name"})'))
|
|
10
12
|
.option('--dev', 'Use development environment', false)
|
|
11
13
|
.action(async (agentId, options) => {
|
|
12
14
|
try {
|
|
@@ -19,21 +21,55 @@ export const copyCommand = new Command('copy')
|
|
|
19
21
|
});
|
|
20
22
|
const spinner = createSpinner('Cloning agent...');
|
|
21
23
|
spinner.start();
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
let result;
|
|
25
|
+
try {
|
|
26
|
+
result = await agent.copyAgent({ id: agentId });
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
spinner.fail('Could not clone the agent');
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
if (result?.success === false) {
|
|
33
|
+
spinner.fail('Could not clone the agent');
|
|
34
|
+
throw new CliError(result.message || 'The API rejected the copy.', 'copy_failed');
|
|
35
|
+
}
|
|
36
|
+
const payload = result?.agent || result?.data || result || {};
|
|
37
|
+
const newId = result?.agentId || payload?.agentId || payload?.id;
|
|
38
|
+
let newName = payload?.name;
|
|
39
|
+
// La respuesta puede no traer el nombre: se consulta el agente nuevo (solo informativo)
|
|
40
|
+
if (newId && !newName) {
|
|
41
|
+
try {
|
|
42
|
+
const res = await agent.getAgentById({ id: newId });
|
|
43
|
+
newName = (res?.agent || res?.data || res)?.name;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Cosmetico: el clon ya se creo
|
|
47
|
+
}
|
|
27
48
|
}
|
|
28
|
-
|
|
49
|
+
spinner.succeed(newName ? `Agent cloned as "${newName}"` : 'Agent cloned');
|
|
50
|
+
if (isJsonMode()) {
|
|
51
|
+
printJson({ sourceId: agentId, id: newId ?? null, name: newName ?? null });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
logger.title('Cloned agent');
|
|
55
|
+
logger.label('Source ID', agentId);
|
|
56
|
+
logger.label('New ID', newId || '—');
|
|
57
|
+
logger.label('Name', newName || '—');
|
|
58
|
+
console.log();
|
|
59
|
+
logger.dim('The cloned agent has the widget disabled.');
|
|
60
|
+
if (newId)
|
|
61
|
+
logger.dim(`Test it with: plazbot agent chat ${newId}`);
|
|
29
62
|
}
|
|
30
63
|
catch (error) {
|
|
31
|
-
|
|
32
|
-
logger.error(message);
|
|
33
|
-
process.exit(1);
|
|
64
|
+
fail(error);
|
|
34
65
|
}
|
|
35
66
|
});
|
|
36
67
|
addExamples(copyCommand, [
|
|
37
|
-
{ description: 'Clone an agent (widget stays disabled on the copy)',
|
|
38
|
-
command: 'plazbot agent copy
|
|
68
|
+
{ description: 'Clone an agent (the widget stays disabled on the copy)',
|
|
69
|
+
command: 'plazbot agent copy age_AbcDef123' },
|
|
70
|
+
{ description: 'Clone and capture the new ID (scripts)',
|
|
71
|
+
command: "plazbot agent copy age_AbcDef123 --json | jq -r '.id'" },
|
|
72
|
+
]);
|
|
73
|
+
addNotes(copyCommand, [
|
|
74
|
+
'To copy an agent to another workspace use: plazbot agent cross-copy',
|
|
39
75
|
]);
|