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,30 +1,30 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { addExamples } from '../../utils/help.js';
|
|
2
|
+
import { addExamples, addNotes } from '../../utils/help.js';
|
|
3
3
|
import { getCommand } from './get.js';
|
|
4
4
|
import { deleteCommand } from './delete.js';
|
|
5
5
|
import { createCommand } from './create.js';
|
|
6
6
|
import { updateCommand } from './update.js';
|
|
7
|
-
import { addAgentCommand } from './add-agent.js';
|
|
7
|
+
import { addAgentCommand, removeAgentCommand } from './add-agent.js';
|
|
8
8
|
import { listCommand } from './list.js';
|
|
9
9
|
import { addLinkCommand } from './add-link.js';
|
|
10
10
|
import { clearLinksCommand } from './clear-links.js';
|
|
11
11
|
export const portalCommands = new Command('portal')
|
|
12
|
-
.description('
|
|
12
|
+
.description('Manage the public web portal where customers chat with your agents')
|
|
13
13
|
.addCommand(listCommand)
|
|
14
14
|
.addCommand(getCommand)
|
|
15
|
-
.addCommand(deleteCommand)
|
|
16
15
|
.addCommand(createCommand)
|
|
17
16
|
.addCommand(updateCommand)
|
|
17
|
+
.addCommand(deleteCommand)
|
|
18
18
|
.addCommand(addAgentCommand)
|
|
19
|
+
.addCommand(removeAgentCommand)
|
|
19
20
|
.addCommand(addLinkCommand)
|
|
20
21
|
.addCommand(clearLinksCommand);
|
|
21
22
|
addExamples(portalCommands, [
|
|
22
|
-
{ description: '
|
|
23
|
-
|
|
24
|
-
{ description: '
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
command: 'plazbot portal add-link prt_AbcDef123 -t "Docs" -u https://docs.example.com' },
|
|
23
|
+
{ description: 'Show the portal of the active workspace', command: 'plazbot portal list' },
|
|
24
|
+
{ description: 'Create a portal', command: 'plazbot portal create -n "Help Center" -t "Welcome"' },
|
|
25
|
+
{ description: 'Attach an agent to the portal', command: 'plazbot portal add-agent por_AbcDef123 -a age_AbcDef123' },
|
|
26
|
+
{ description: 'Add a link to the portal', command: 'plazbot portal add-link por_AbcDef123 -t "Docs" -u https://docs.example.com' },
|
|
27
|
+
]);
|
|
28
|
+
addNotes(portalCommands, [
|
|
29
|
+
'Each workspace has at most one portal.',
|
|
30
30
|
]);
|
|
@@ -1,68 +1,50 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { addExamples, addNotes } from '../../utils/help.js';
|
|
3
|
+
import { jsonOption, isJsonMode, printJson, fail, CliError } from '../../utils/output.js';
|
|
4
|
+
import { createSpinner } from '../../utils/ui.js';
|
|
5
|
+
import { createPortalClient, printPortalDetails } from './shared.js';
|
|
6
6
|
export const listCommand = new Command('list')
|
|
7
|
-
.description('Show the portal
|
|
7
|
+
.description('Show the portal of the active workspace (one portal per workspace)')
|
|
8
|
+
.addOption(jsonOption())
|
|
8
9
|
.option('--dev', 'Use development environment', false)
|
|
9
10
|
.action(async (options) => {
|
|
10
11
|
try {
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
...(options.dev && { customUrl: "http://localhost:5090" })
|
|
18
|
-
});
|
|
19
|
-
const response = await portal.getExistsPortal();
|
|
20
|
-
const portalDetails = response.portal;
|
|
21
|
-
logger.title('Portal found');
|
|
22
|
-
logger.label('ID', portalDetails.id);
|
|
23
|
-
logger.label('Name', portalDetails.name);
|
|
24
|
-
logger.label('URL', portalDetails.url);
|
|
25
|
-
logger.label('Access', portalDetails.access || 'N/A');
|
|
26
|
-
logger.label('Status', portalDetails.disabled ? 'Disabled' : 'Enabled');
|
|
27
|
-
if (portalDetails.title) {
|
|
28
|
-
logger.label('Title', portalDetails.title);
|
|
12
|
+
const { portal } = await createPortalClient(options.dev);
|
|
13
|
+
const spinner = createSpinner('Loading portal...').start();
|
|
14
|
+
let details;
|
|
15
|
+
try {
|
|
16
|
+
const response = await portal.getExistsPortal();
|
|
17
|
+
details = response?.portal || undefined;
|
|
29
18
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
logger.label('Logo', portalDetails.logo);
|
|
36
|
-
if (portalDetails.logodark) {
|
|
37
|
-
logger.label('Logo (dark mode)', portalDetails.logodark);
|
|
19
|
+
catch (error) {
|
|
20
|
+
const message = error instanceof Error ? error.message : '';
|
|
21
|
+
if (!/not found/i.test(message)) {
|
|
22
|
+
spinner.stop();
|
|
23
|
+
throw error;
|
|
38
24
|
}
|
|
39
25
|
}
|
|
40
|
-
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
if (portalAny.agents && portalAny.agents.length > 0) {
|
|
48
|
-
logger.title('Associated agents');
|
|
49
|
-
portalAny.agents.forEach((agent) => {
|
|
50
|
-
logger.label(agent.name, agent.id);
|
|
51
|
-
});
|
|
26
|
+
spinner.stop();
|
|
27
|
+
if (!details) {
|
|
28
|
+
if (isJsonMode())
|
|
29
|
+
printJson(null);
|
|
30
|
+
throw new CliError('This workspace has no portal yet.', 'portal_not_found', 'Create one with: plazbot portal create -n "Help Center" -t "Welcome"');
|
|
52
31
|
}
|
|
53
|
-
if (
|
|
54
|
-
|
|
32
|
+
if (isJsonMode()) {
|
|
33
|
+
printJson(details);
|
|
34
|
+
return;
|
|
55
35
|
}
|
|
36
|
+
printPortalDetails(details, 'Workspace portal');
|
|
37
|
+
console.log();
|
|
56
38
|
}
|
|
57
39
|
catch (error) {
|
|
58
|
-
|
|
59
|
-
logger.error(message);
|
|
60
|
-
process.exit(1);
|
|
40
|
+
fail(error);
|
|
61
41
|
}
|
|
62
42
|
});
|
|
63
43
|
addExamples(listCommand, [
|
|
64
|
-
{ description: 'Show the portal of the active workspace',
|
|
65
|
-
|
|
66
|
-
{ description: '
|
|
67
|
-
|
|
44
|
+
{ description: 'Show the portal of the active workspace', command: 'plazbot portal list' },
|
|
45
|
+
{ description: 'Get the portal as JSON (null when there is none)', command: 'plazbot portal list --json' },
|
|
46
|
+
{ description: 'Extract only the portal ID', command: "plazbot portal list --json | jq -r '.id'" },
|
|
47
|
+
]);
|
|
48
|
+
addNotes(listCommand, [
|
|
49
|
+
'A workspace can have a single portal. Exits with code 1 when there is none.',
|
|
68
50
|
]);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Helpers shared by the `plazbot portal` subcommands.
|
|
2
|
+
import { Portal } from 'plazbot';
|
|
3
|
+
import { getStoredCredentials } from '../../utils/credentials.js';
|
|
4
|
+
import { logger } from '../../utils/logger.js';
|
|
5
|
+
import { CliError } from '../../utils/output.js';
|
|
6
|
+
/** Builds an SDK Portal client from the active session. */
|
|
7
|
+
export async function createPortalClient(dev) {
|
|
8
|
+
const credentials = await getStoredCredentials();
|
|
9
|
+
const portal = new Portal({
|
|
10
|
+
workspaceId: credentials.workspace,
|
|
11
|
+
apiKey: credentials.apiKey,
|
|
12
|
+
zone: credentials.zone,
|
|
13
|
+
...(dev && { customUrl: 'http://localhost:5090' }),
|
|
14
|
+
});
|
|
15
|
+
return { portal, credentials };
|
|
16
|
+
}
|
|
17
|
+
/** Resolves the portal ID from the positional argument or a legacy option. */
|
|
18
|
+
export function resolvePortalId(positional, legacy) {
|
|
19
|
+
const id = (positional || legacy || '').trim();
|
|
20
|
+
if (!id) {
|
|
21
|
+
throw new CliError('Portal ID is required.', 'missing_portal_id', "Pass it as the first argument. Find it with 'plazbot portal list'.");
|
|
22
|
+
}
|
|
23
|
+
return id;
|
|
24
|
+
}
|
|
25
|
+
/** The SDK only accepts PNG/JPG logos given as an absolute URL or a path starting with "/". */
|
|
26
|
+
export function validateLogo(value, flag) {
|
|
27
|
+
const url = value.trim();
|
|
28
|
+
if (!/^(https?:\/\/|\/)/i.test(url) || !/\.(png|jpg)$/i.test(url)) {
|
|
29
|
+
throw new CliError(`Invalid logo for ${flag}: "${value}".`, 'invalid_logo', 'Use a .png or .jpg URL (https://...) or a path starting with "/".');
|
|
30
|
+
}
|
|
31
|
+
return url;
|
|
32
|
+
}
|
|
33
|
+
export function validateAccess(value) {
|
|
34
|
+
const access = value.trim().toLowerCase();
|
|
35
|
+
if (access !== 'direct' && access !== 'form') {
|
|
36
|
+
throw new CliError(`Invalid access type "${value}".`, 'invalid_access', 'Use "direct" or "form".');
|
|
37
|
+
}
|
|
38
|
+
return access;
|
|
39
|
+
}
|
|
40
|
+
export function validateHttpUrl(value, flag) {
|
|
41
|
+
const url = value.trim();
|
|
42
|
+
let parsed;
|
|
43
|
+
try {
|
|
44
|
+
parsed = new URL(url);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new CliError(`Invalid URL for ${flag}: "${value}".`, 'invalid_url', 'Use a full URL starting with http:// or https://.');
|
|
48
|
+
}
|
|
49
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
50
|
+
throw new CliError(`Invalid URL for ${flag}: "${value}".`, 'invalid_url', 'Only http:// and https:// URLs are allowed.');
|
|
51
|
+
}
|
|
52
|
+
return url;
|
|
53
|
+
}
|
|
54
|
+
/** Parses a `true|false` flag value (also accepts yes/no, on/off, 1/0). */
|
|
55
|
+
export function parseBoolFlag(value, flag) {
|
|
56
|
+
const v = value.trim().toLowerCase();
|
|
57
|
+
if (['true', 'yes', 'on', '1'].includes(v))
|
|
58
|
+
return true;
|
|
59
|
+
if (['false', 'no', 'off', '0'].includes(v))
|
|
60
|
+
return false;
|
|
61
|
+
throw new CliError(`Invalid value for ${flag}: "${value}".`, 'invalid_boolean', `Use ${flag} true or ${flag} false.`);
|
|
62
|
+
}
|
|
63
|
+
/** Pretty-prints a portal (human mode). */
|
|
64
|
+
export function printPortalDetails(details, title = 'Portal') {
|
|
65
|
+
logger.title(title);
|
|
66
|
+
logger.label('ID', details.id);
|
|
67
|
+
logger.label('Name', details.name);
|
|
68
|
+
logger.label('URL', details.url);
|
|
69
|
+
logger.label('Access', details.access || 'direct');
|
|
70
|
+
logger.label('Status', details.disabled ? 'Disabled' : 'Enabled');
|
|
71
|
+
if (details.title)
|
|
72
|
+
logger.label('Title', details.title);
|
|
73
|
+
if (details.subtitle)
|
|
74
|
+
logger.label('Subtitle', details.subtitle);
|
|
75
|
+
if (typeof details.brandOff === 'boolean')
|
|
76
|
+
logger.label('Plazbot brand', details.brandOff ? 'Hidden' : 'Visible');
|
|
77
|
+
if (details.logo || details.logodark) {
|
|
78
|
+
logger.title('Assets');
|
|
79
|
+
if (details.logo)
|
|
80
|
+
logger.label('Logo', details.logo);
|
|
81
|
+
if (details.logodark)
|
|
82
|
+
logger.label('Logo (dark mode)', details.logodark);
|
|
83
|
+
}
|
|
84
|
+
if (details.links && details.links.length > 0) {
|
|
85
|
+
logger.title('Links');
|
|
86
|
+
details.links.forEach((link) => logger.label(link.value, link.url));
|
|
87
|
+
}
|
|
88
|
+
if (details.agents && details.agents.length > 0) {
|
|
89
|
+
logger.title('Agents');
|
|
90
|
+
details.agents.forEach((agent) => logger.label(agent.name, agent.id));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -1,86 +1,116 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
2
|
+
import { addExamples, addNotes } from '../../utils/help.js';
|
|
3
|
+
import { jsonOption, isJsonMode, printJson, fail, CliError } from '../../utils/output.js';
|
|
4
|
+
import { createSpinner } from '../../utils/ui.js';
|
|
4
5
|
import { logger } from '../../utils/logger.js';
|
|
5
|
-
import {
|
|
6
|
+
import { createPortalClient, parseBoolFlag, resolvePortalId, validateAccess, validateLogo } from './shared.js';
|
|
6
7
|
export const updateCommand = new Command('update')
|
|
7
|
-
.description('Update
|
|
8
|
-
.argument('<portalId>', 'ID
|
|
8
|
+
.description('Update a portal (only the fields you pass are changed)')
|
|
9
|
+
.argument('<portalId>', 'Portal ID (see plazbot portal list)')
|
|
9
10
|
.option('-n, --name <name>', 'New portal name')
|
|
10
|
-
.option('-t, --title <title>', 'New
|
|
11
|
-
.option('-s, --subtitle <subtitle>', 'New
|
|
12
|
-
.option('-l, --logo <url>', 'New logo URL')
|
|
13
|
-
.option('-d, --logo-dark <url>', 'New dark-mode logo URL')
|
|
14
|
-
.option('-a, --access <type>', 'Access type
|
|
15
|
-
.option('--disabled <
|
|
16
|
-
.option('--brand-off <
|
|
11
|
+
.option('-t, --title <title>', 'New welcome title')
|
|
12
|
+
.option('-s, --subtitle <subtitle>', 'New subtitle')
|
|
13
|
+
.option('-l, --logo <url>', 'New logo URL (.png or .jpg)')
|
|
14
|
+
.option('-d, --logo-dark <url>', 'New dark-mode logo URL (.png or .jpg)')
|
|
15
|
+
.option('-a, --access <type>', 'Access type: direct or form')
|
|
16
|
+
.option('--disabled <true|false>', 'Disable (true) or enable (false) the portal')
|
|
17
|
+
.option('--brand-off <true|false>', 'Hide (true) or show (false) the Plazbot brand')
|
|
18
|
+
.addOption(new Option('--enable', 'Shortcut for --disabled false').conflicts(['disabled', 'disable']))
|
|
19
|
+
.addOption(new Option('--disable', 'Shortcut for --disabled true').conflicts(['disabled', 'enable']))
|
|
20
|
+
.addOption(jsonOption())
|
|
17
21
|
.option('--dev', 'Use development environment', false)
|
|
18
|
-
.action(async (
|
|
22
|
+
.action(async (portalIdArg, options) => {
|
|
19
23
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
28
|
-
// Obtener detalles actuales del portal
|
|
29
|
-
const currentPortal = await portal.getPortal(portalId);
|
|
30
|
-
logger.title('Current portal');
|
|
31
|
-
logger.json(currentPortal.portal);
|
|
32
|
-
// Construir objeto de actualización solo con los campos proporcionados
|
|
33
|
-
const updateData = { id: portalId };
|
|
34
|
-
if (options.name)
|
|
35
|
-
updateData.name = options.name;
|
|
36
|
-
if (options.title)
|
|
37
|
-
updateData.title = options.title;
|
|
38
|
-
if (options.subtitle)
|
|
39
|
-
updateData.subtitle = options.subtitle;
|
|
40
|
-
if (options.logo)
|
|
41
|
-
updateData.logo = options.logo;
|
|
42
|
-
if (options.logoDark)
|
|
43
|
-
updateData.logodark = options.logoDark;
|
|
44
|
-
if (options.access) {
|
|
45
|
-
if (options.access !== 'direct' && options.access !== 'form') {
|
|
46
|
-
throw new Error('Access type must be "direct" or "form"');
|
|
47
|
-
}
|
|
48
|
-
updateData.access = options.access;
|
|
24
|
+
const portalId = resolvePortalId(portalIdArg);
|
|
25
|
+
// Validate everything before calling the API.
|
|
26
|
+
const changes = {};
|
|
27
|
+
if (options.name !== undefined) {
|
|
28
|
+
if (!options.name.trim())
|
|
29
|
+
throw new CliError('Portal name cannot be empty.', 'invalid_name');
|
|
30
|
+
changes.name = options.name.trim();
|
|
49
31
|
}
|
|
50
|
-
if (options.
|
|
51
|
-
if (options.
|
|
52
|
-
throw new
|
|
53
|
-
|
|
54
|
-
updateData.disabled = options.disabled === 'true';
|
|
32
|
+
if (options.title !== undefined) {
|
|
33
|
+
if (!options.title.trim())
|
|
34
|
+
throw new CliError('Portal title cannot be empty.', 'invalid_title');
|
|
35
|
+
changes.title = options.title.trim();
|
|
55
36
|
}
|
|
56
|
-
if (options.
|
|
57
|
-
if (options.
|
|
58
|
-
throw new
|
|
37
|
+
if (options.subtitle !== undefined) {
|
|
38
|
+
if (!options.subtitle.trim()) {
|
|
39
|
+
throw new CliError('Subtitle cannot be empty.', 'invalid_subtitle', 'The API ignores empty values, so a subtitle cannot be cleared from the CLI.');
|
|
59
40
|
}
|
|
60
|
-
|
|
41
|
+
changes.subtitle = options.subtitle.trim();
|
|
61
42
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
43
|
+
if (options.logo !== undefined)
|
|
44
|
+
changes.logo = validateLogo(options.logo, '--logo');
|
|
45
|
+
if (options.logoDark !== undefined)
|
|
46
|
+
changes.logodark = validateLogo(options.logoDark, '--logo-dark');
|
|
47
|
+
if (options.access !== undefined)
|
|
48
|
+
changes.access = validateAccess(options.access);
|
|
49
|
+
if (options.disabled !== undefined)
|
|
50
|
+
changes.disabled = parseBoolFlag(options.disabled, '--disabled');
|
|
51
|
+
if (options.enable)
|
|
52
|
+
changes.disabled = false;
|
|
53
|
+
if (options.disable)
|
|
54
|
+
changes.disabled = true;
|
|
55
|
+
if (options.brandOff !== undefined)
|
|
56
|
+
changes.brandOff = parseBoolFlag(options.brandOff, '--brand-off');
|
|
57
|
+
if (Object.keys(changes).length === 0) {
|
|
58
|
+
throw new CliError('Nothing to update.', 'nothing_to_update', 'Pass at least one option, e.g. -n "New name", -t "New title" or --disable.');
|
|
69
59
|
}
|
|
60
|
+
const { portal } = await createPortalClient(options.dev);
|
|
61
|
+
const spinner = createSpinner('Updating portal...').start();
|
|
62
|
+
const response = (await portal.getPortal(portalId));
|
|
63
|
+
const current = response.portal || {};
|
|
64
|
+
// The API (and SDK) reset access=direct, theme=light, disabled=false and brandOff=false
|
|
65
|
+
// when they are missing from the request, so the current values are always sent.
|
|
66
|
+
const updateData = { id: portalId };
|
|
67
|
+
if (current.access)
|
|
68
|
+
updateData.access = current.access;
|
|
69
|
+
if (current.theme)
|
|
70
|
+
updateData.theme = current.theme;
|
|
71
|
+
if (typeof current.disabled === 'boolean')
|
|
72
|
+
updateData.disabled = current.disabled;
|
|
73
|
+
if (typeof current.brandOff === 'boolean')
|
|
74
|
+
updateData.brandOff = current.brandOff;
|
|
75
|
+
Object.assign(updateData, changes);
|
|
76
|
+
await portal.updatePortal(updateData);
|
|
77
|
+
spinner.succeed(`Portal "${changes.name || current.name || portalId}" updated`);
|
|
78
|
+
if (isJsonMode()) {
|
|
79
|
+
printJson({ id: portalId, updated: true, changes });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const fieldLabels = {
|
|
83
|
+
name: 'Name', title: 'Title', subtitle: 'Subtitle', logo: 'Logo', logodark: 'Logo (dark mode)',
|
|
84
|
+
access: 'Access', disabled: 'Status', brandOff: 'Plazbot brand',
|
|
85
|
+
};
|
|
86
|
+
const show = (key, value) => {
|
|
87
|
+
if (value === undefined || value === null || value === '')
|
|
88
|
+
return '(empty)';
|
|
89
|
+
if (key === 'disabled')
|
|
90
|
+
return value ? 'Disabled' : 'Enabled';
|
|
91
|
+
if (key === 'brandOff')
|
|
92
|
+
return value ? 'Hidden' : 'Visible';
|
|
93
|
+
return String(value);
|
|
94
|
+
};
|
|
95
|
+
logger.title('Changes');
|
|
96
|
+
for (const [key, value] of Object.entries(changes)) {
|
|
97
|
+
const before = show(key, current[key]);
|
|
98
|
+
const after = show(key, value);
|
|
99
|
+
logger.label(fieldLabels[key] || key, before === after ? after : `${before} → ${after}`);
|
|
100
|
+
}
|
|
101
|
+
console.log();
|
|
70
102
|
}
|
|
71
103
|
catch (error) {
|
|
72
|
-
|
|
73
|
-
logger.error(message);
|
|
74
|
-
process.exit(1);
|
|
104
|
+
fail(error);
|
|
75
105
|
}
|
|
76
106
|
});
|
|
77
107
|
addExamples(updateCommand, [
|
|
78
|
-
{ description: 'Rename a portal',
|
|
79
|
-
|
|
80
|
-
{ description: '
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
108
|
+
{ description: 'Rename a portal', command: 'plazbot portal update por_AbcDef123 -n "Support Center"' },
|
|
109
|
+
{ description: 'Ask for contact details and change the title', command: 'plazbot portal update por_AbcDef123 -a form -t "Sign in to continue"' },
|
|
110
|
+
{ description: 'Disable a portal', command: 'plazbot portal update por_AbcDef123 --disable' },
|
|
111
|
+
{ description: 'Hide the Plazbot brand and change the logo', command: 'plazbot portal update por_AbcDef123 --brand-off true -l https://files.example.com/logo.png' },
|
|
112
|
+
]);
|
|
113
|
+
addNotes(updateCommand, [
|
|
114
|
+
'Fields you do not pass keep their current value (access, status and brand included).',
|
|
115
|
+
'--disabled and --brand-off take true or false (yes/no and on/off also work).',
|
|
86
116
|
]);
|