plazbot-cli 0.4.0 → 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.
Files changed (76) hide show
  1. package/README.md +67 -111
  2. package/dist/cli.js +26 -11
  3. package/dist/commands/agent/ai-config.js +264 -63
  4. package/dist/commands/agent/chat.js +16 -11
  5. package/dist/commands/agent/copy.js +49 -13
  6. package/dist/commands/agent/create.js +62 -17
  7. package/dist/commands/agent/delete.js +50 -39
  8. package/dist/commands/agent/enable-widget.js +47 -27
  9. package/dist/commands/agent/export.js +179 -88
  10. package/dist/commands/agent/files.js +155 -73
  11. package/dist/commands/agent/get.js +24 -16
  12. package/dist/commands/agent/index.js +12 -10
  13. package/dist/commands/agent/list.js +20 -11
  14. package/dist/commands/agent/monitor.js +101 -53
  15. package/dist/commands/agent/on-message.js +55 -32
  16. package/dist/commands/agent/set.js +230 -82
  17. package/dist/commands/agent/templates.js +259 -156
  18. package/dist/commands/agent/tools.js +356 -123
  19. package/dist/commands/agent/update.js +69 -28
  20. package/dist/commands/agent/validate.js +72 -40
  21. package/dist/commands/agent/wizard.js +240 -329
  22. package/dist/commands/auth/browser-login.js +37 -31
  23. package/dist/commands/auth/login.js +49 -45
  24. package/dist/commands/auth/logout.js +20 -11
  25. package/dist/commands/auth/status.js +36 -12
  26. package/dist/commands/auth/workspace.js +61 -27
  27. package/dist/commands/portal/add-agent.js +106 -45
  28. package/dist/commands/portal/add-link.js +25 -28
  29. package/dist/commands/portal/clear-links.js +39 -19
  30. package/dist/commands/portal/create.js +66 -37
  31. package/dist/commands/portal/delete.js +40 -45
  32. package/dist/commands/portal/get.js +21 -54
  33. package/dist/commands/portal/index.js +12 -12
  34. package/dist/commands/portal/list.js +34 -52
  35. package/dist/commands/portal/shared.js +92 -0
  36. package/dist/commands/portal/update.js +100 -70
  37. package/dist/commands/whatsapp/broadcast.js +169 -77
  38. package/dist/commands/whatsapp/channels.js +167 -65
  39. package/dist/commands/whatsapp/chat.js +45 -46
  40. package/dist/commands/whatsapp/connect-flow.js +16 -15
  41. package/dist/commands/whatsapp/connect.js +36 -21
  42. package/dist/commands/whatsapp/delete-webhook.js +42 -25
  43. package/dist/commands/whatsapp/index.js +15 -16
  44. package/dist/commands/whatsapp/register-webhook.js +43 -26
  45. package/dist/commands/whatsapp/send-template.js +62 -61
  46. package/dist/commands/whatsapp/send.js +46 -33
  47. package/dist/commands/whatsapp/setup.js +62 -52
  48. package/dist/commands/whatsapp/shared.js +75 -0
  49. package/dist/commands/whatsapp/widget.js +82 -45
  50. package/dist/commands/workers/deploy.js +80 -58
  51. package/dist/commands/workers/index.js +8 -8
  52. package/dist/commands/workers/list.js +42 -20
  53. package/dist/commands/workers/logs.js +139 -36
  54. package/dist/commands/workers/remove.js +45 -47
  55. package/dist/commands/workers/secret.js +159 -83
  56. package/dist/commands/workers/test.js +108 -60
  57. package/dist/schemas/agent.config.schema.json +183 -8
  58. package/dist/studio/api/sseClient.js +1 -1
  59. package/dist/studio/api/studioApi.js +8 -0
  60. package/dist/studio/components/App.js +12 -0
  61. package/dist/studio/components/Footer.js +1 -1
  62. package/dist/studio/index.js +77 -14
  63. package/dist/studio/runOneShot.js +62 -23
  64. package/dist/studio/runRepl.js +5 -5
  65. package/dist/studio/slash/handlers.js +11 -8
  66. package/dist/studio/state/store.js +1 -1
  67. package/dist/utils/agent-errors.js +14 -11
  68. package/dist/utils/agent-patch.js +60 -0
  69. package/dist/utils/banner.js +9 -5
  70. package/dist/utils/confirm.js +31 -0
  71. package/dist/utils/credentials.js +13 -3
  72. package/dist/utils/logger.js +13 -10
  73. package/dist/utils/output.js +78 -0
  74. package/dist/utils/phone.js +22 -0
  75. package/dist/utils/ui.js +14 -11
  76. package/package.json +2 -2
@@ -1,55 +1,116 @@
1
- import { Command } from 'commander';
2
- import { Portal, Agent } from 'plazbot';
3
- import { getStoredCredentials } from '../../utils/credentials.js';
1
+ import { Command, Option } from 'commander';
2
+ import { Agent } from 'plazbot';
3
+ import { addExamples, addNotes } from '../../utils/help.js';
4
+ import { jsonOption, isJsonMode, printJson, fail, CliError } from '../../utils/output.js';
5
+ import { addYesOption, confirmAction } from '../../utils/confirm.js';
6
+ import { createSpinner } from '../../utils/ui.js';
4
7
  import { logger } from '../../utils/logger.js';
5
- import { addExamples } from '../../utils/help.js';
6
- export const addAgentCommand = new Command('add-agent-portal')
7
- .description('Add an existing agent to a portal')
8
- .requiredOption('-p, --portal-id <portalId>', 'Portal ID')
9
- .requiredOption('-a, --agent-id <agentId>', 'ID of the agent to add')
8
+ import { createPortalClient, resolvePortalId } from './shared.js';
9
+ function resolveAgentId(options) {
10
+ const id = (options.agent || options.agentId || '').trim();
11
+ if (!id) {
12
+ throw new CliError('Agent ID is required.', 'missing_agent_id', "Pass it with -a, --agent <agentId>. Find it with 'plazbot agent list'.");
13
+ }
14
+ return id;
15
+ }
16
+ /** Loads the agent name (the SDK returns { success, agent }). */
17
+ async function fetchAgentName(client, agentId, dev) {
18
+ const agent = new Agent({
19
+ workspaceId: client.credentials.workspace,
20
+ apiKey: client.credentials.apiKey,
21
+ zone: client.credentials.zone,
22
+ ...(dev && { customUrl: 'http://localhost:5090' }),
23
+ });
24
+ const response = await agent.getAgentById({ id: agentId });
25
+ const data = response?.agent || response?.data || response;
26
+ return data?.name;
27
+ }
28
+ const portalIdOption = () => new Option('-p, --portal-id <portalId>', 'Portal ID (legacy, use the positional argument)').hideHelp();
29
+ export const addAgentCommand = new Command('add-agent')
30
+ .alias('add-agent-portal')
31
+ .description('Attach an existing agent to a portal')
32
+ .argument('[portalId]', 'Portal ID (see plazbot portal list)')
33
+ .option('-a, --agent <agentId>', 'ID of the agent to attach (see plazbot agent list)')
34
+ .addOption(portalIdOption())
35
+ .addOption(new Option('--agent-id <agentId>', 'Alias of --agent').hideHelp())
36
+ .addOption(jsonOption())
10
37
  .option('--dev', 'Use development environment', false)
11
- .action(async (options) => {
38
+ .action(async (portalIdArg, options) => {
12
39
  try {
13
- // Obtener credenciales guardadas
14
- const credentials = await getStoredCredentials();
15
- const portal = new Portal({
16
- workspaceId: credentials.workspace,
17
- apiKey: credentials.apiKey,
18
- zone: credentials.zone,
19
- ...(options.dev && { customUrl: "http://localhost:5090" })
20
- });
21
- // Obtener detalles del portal
22
- const portalDetails = await portal.getPortal(options.portalId);
23
- // Crear instancia del agente para obtener sus detalles
24
- const agent = new Agent({
25
- workspaceId: credentials.workspace,
26
- apiKey: credentials.apiKey,
27
- zone: credentials.zone,
28
- ...(options.dev && { customUrl: "http://localhost:5090" })
29
- });
30
- // Obtener detalles del agente
31
- const agentDetails = await agent.getAgentById({ id: options.agentId });
32
- logger.title('Operation details');
33
- logger.label('Portal ID', portalDetails.portal?.id || options.portalId);
34
- logger.label('Portal Name', portalDetails.portal?.name || 'N/A');
35
- logger.label('Agent ID', agentDetails.id);
36
- logger.label('Agent Name', agentDetails.name);
37
- await portal.addAgentToPortal({
38
- portalId: options.portalId,
39
- id: options.agentId
40
- });
41
- logger.success('Agent added to the portal successfully');
42
- if (options.dev) {
43
- logger.warning('Environment: development');
40
+ const portalId = resolvePortalId(portalIdArg, options.portalId);
41
+ const agentId = resolveAgentId(options);
42
+ const client = await createPortalClient(options.dev);
43
+ const spinner = createSpinner('Attaching agent...').start();
44
+ const [portalRes, agentName] = await Promise.all([
45
+ client.portal.getPortal(portalId),
46
+ fetchAgentName(client, agentId, options.dev),
47
+ ]);
48
+ const portalName = portalRes?.portal?.name || portalId;
49
+ if (portalRes?.portal?.agents?.some(a => a.id === agentId)) {
50
+ spinner.info(`Agent "${agentName || agentId}" is already attached to portal "${portalName}"`);
51
+ if (isJsonMode())
52
+ printJson({ portalId, agentId, added: false, alreadyAttached: true });
53
+ return;
54
+ }
55
+ await client.portal.addAgentToPortal({ portalId, id: agentId });
56
+ spinner.succeed(`Agent "${agentName || agentId}" attached to portal "${portalName}"`);
57
+ if (isJsonMode()) {
58
+ printJson({ portalId, agentId, agentName: agentName ?? null, added: true });
59
+ return;
44
60
  }
61
+ console.log();
45
62
  }
46
63
  catch (error) {
47
- const message = error instanceof Error ? error.message : 'Unknown error';
48
- logger.error(message);
49
- process.exit(1);
64
+ fail(error);
50
65
  }
51
66
  });
52
67
  addExamples(addAgentCommand, [
53
- { description: 'Attach an existing agent to a portal',
54
- command: 'plazbot portal add-agent-portal -p prt_AbcDef123 -a agt_AbcDef123' },
68
+ { description: 'Attach an agent to a portal', command: 'plazbot portal add-agent por_AbcDef123 -a age_AbcDef123' },
69
+ { description: 'Attach and get the result as JSON', command: 'plazbot portal add-agent por_AbcDef123 -a age_AbcDef123 --json' },
70
+ ]);
71
+ addNotes(addAgentCommand, [
72
+ 'The agent must already exist in the active workspace (plazbot agent list).',
73
+ 'Old form still works: plazbot portal add-agent-portal -p <portalId> -a <agentId>.',
74
+ ]);
75
+ export const removeAgentCommand = new Command('remove-agent')
76
+ .description('Detach an agent from a portal (the agent itself is not deleted)')
77
+ .argument('[portalId]', 'Portal ID (see plazbot portal list)')
78
+ .option('-a, --agent <agentId>', 'ID of the agent to detach')
79
+ .addOption(portalIdOption())
80
+ .addOption(new Option('--agent-id <agentId>', 'Alias of --agent').hideHelp())
81
+ .addOption(jsonOption())
82
+ .option('--dev', 'Use development environment', false);
83
+ addYesOption(removeAgentCommand);
84
+ removeAgentCommand.action(async (portalIdArg, options) => {
85
+ try {
86
+ const portalId = resolvePortalId(portalIdArg, options.portalId);
87
+ const agentId = resolveAgentId(options);
88
+ const client = await createPortalClient(options.dev);
89
+ const spinner = createSpinner('Loading portal...').start();
90
+ const portalRes = (await client.portal.getPortal(portalId));
91
+ spinner.stop();
92
+ const portalName = portalRes?.portal?.name || portalId;
93
+ const attached = portalRes?.portal?.agents?.find(a => a.id === agentId);
94
+ if (portalRes?.portal?.agents && !attached) {
95
+ throw new CliError(`Agent ${agentId} is not attached to portal "${portalName}".`, 'agent_not_attached', `See the attached agents with 'plazbot portal get ${portalId}'.`);
96
+ }
97
+ const agentLabel = attached?.name || agentId;
98
+ const ok = await confirmAction(`Detach agent "${agentLabel}" from portal "${portalName}"?`, options);
99
+ if (!ok) {
100
+ logger.dim('Aborted.');
101
+ process.exit(1);
102
+ }
103
+ const removing = createSpinner('Detaching agent...').start();
104
+ await client.portal.removeAgentFromPortal({ portalId, id: agentId });
105
+ removing.succeed(`Agent "${agentLabel}" detached from portal "${portalName}"`);
106
+ if (isJsonMode())
107
+ printJson({ portalId, agentId, removed: true });
108
+ }
109
+ catch (error) {
110
+ fail(error);
111
+ }
112
+ });
113
+ addExamples(removeAgentCommand, [
114
+ { description: 'Detach an agent from a portal (asks for confirmation)', command: 'plazbot portal remove-agent por_AbcDef123 -a age_AbcDef123' },
115
+ { description: 'Detach without confirmation (scripts and CI)', command: 'plazbot portal remove-agent por_AbcDef123 -a age_AbcDef123 --yes' },
55
116
  ]);
@@ -1,38 +1,35 @@
1
1
  import { Command } from 'commander';
2
- import { Portal } from 'plazbot';
3
- import { getStoredCredentials } from '../../utils/credentials.js';
4
- import { logger } from '../../utils/logger.js';
5
2
  import { addExamples } 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, resolvePortalId, validateHttpUrl } from './shared.js';
6
6
  export const addLinkCommand = new Command('add-link')
7
- .description('Add a link to the portal')
8
- .argument('<portalId>', 'Portal ID')
9
- .requiredOption('-t, --title <title>', 'Link title')
10
- .requiredOption('-u, --url <url>', 'Link URL')
11
- .option('--dev', 'Use development environment')
12
- .action(async (portalId, options) => {
7
+ .description('Add a link to the portal navigation')
8
+ .argument('<portalId>', 'Portal ID (see plazbot portal list)')
9
+ .requiredOption('-t, --title <title>', 'Link text')
10
+ .requiredOption('-u, --url <url>', 'Link URL (http:// or https://)')
11
+ .addOption(jsonOption())
12
+ .option('--dev', 'Use development environment', false)
13
+ .action(async (portalIdArg, options) => {
13
14
  try {
14
- const credentials = await getStoredCredentials();
15
- const portal = new Portal({
16
- workspaceId: credentials.workspace,
17
- apiKey: credentials.apiKey,
18
- zone: credentials.zone,
19
- ...(options.dev && { customUrl: "http://localhost:5090" })
20
- });
21
- await portal.addLinkToPortal({
22
- portalId,
23
- value: options.title,
24
- url: options.url
25
- });
26
- logger.success('Link added successfully');
15
+ const portalId = resolvePortalId(portalIdArg);
16
+ const title = options.title.trim();
17
+ if (!title)
18
+ throw new CliError('Link title cannot be empty.', 'missing_title', 'Pass it with -t, --title <title>.');
19
+ const url = validateHttpUrl(options.url, '--url');
20
+ const { portal } = await createPortalClient(options.dev);
21
+ const spinner = createSpinner('Adding link...').start();
22
+ await portal.addLinkToPortal({ portalId, value: title, url });
23
+ spinner.succeed(`Link "${title}" added`);
24
+ if (isJsonMode())
25
+ printJson({ portalId, title, url, added: true });
27
26
  }
28
27
  catch (error) {
29
- logger.error(error.message);
30
- process.exit(1);
28
+ fail(error);
31
29
  }
32
30
  });
33
31
  addExamples(addLinkCommand, [
34
- { description: 'Add a documentation link to the portal',
35
- command: 'plazbot portal add-link prt_AbcDef123 -t "Docs" -u https://docs.example.com' },
36
- { description: 'Add a support link',
37
- command: 'plazbot portal add-link prt_AbcDef123 -t "Contact us" -u https://example.com/contact' },
32
+ { description: 'Add a documentation link', command: 'plazbot portal add-link por_AbcDef123 -t "Docs" -u https://docs.example.com' },
33
+ { description: 'Add a contact link', command: 'plazbot portal add-link por_AbcDef123 -t "Contact us" -u https://example.com/contact' },
34
+ { description: 'Add a link and get the result as JSON', command: 'plazbot portal add-link por_AbcDef123 -t "Pricing" -u https://example.com/pricing --json' },
38
35
  ]);
@@ -1,30 +1,50 @@
1
1
  import { Command } from 'commander';
2
- import { Portal } from 'plazbot';
3
- import { getStoredCredentials } from '../../utils/credentials.js';
2
+ import { addExamples, addNotes } from '../../utils/help.js';
3
+ import { jsonOption, isJsonMode, printJson, fail } from '../../utils/output.js';
4
+ import { addYesOption, confirmAction } from '../../utils/confirm.js';
5
+ import { createSpinner } from '../../utils/ui.js';
4
6
  import { logger } from '../../utils/logger.js';
5
- import { addExamples } from '../../utils/help.js';
7
+ import { createPortalClient, resolvePortalId } from './shared.js';
6
8
  export const clearLinksCommand = new Command('clear-links')
7
- .description('Remove all links from the portal')
8
- .argument('<portalId>', 'Portal ID')
9
- .option('--dev', 'Use development environment')
10
- .action(async (portalId, options) => {
9
+ .description('Remove every link from a portal')
10
+ .argument('<portalId>', 'Portal ID (see plazbot portal list)')
11
+ .addOption(jsonOption())
12
+ .option('--dev', 'Use development environment', false);
13
+ addYesOption(clearLinksCommand);
14
+ clearLinksCommand.action(async (portalIdArg, options) => {
11
15
  try {
12
- const credentials = await getStoredCredentials();
13
- const portal = new Portal({
14
- workspaceId: credentials.workspace,
15
- apiKey: credentials.apiKey,
16
- zone: credentials.zone,
17
- ...(options.dev && { customUrl: "http://localhost:5090" })
18
- });
16
+ const portalId = resolvePortalId(portalIdArg);
17
+ const { portal } = await createPortalClient(options.dev);
18
+ const spinner = createSpinner('Loading portal...').start();
19
+ const response = (await portal.getPortal(portalId));
20
+ spinner.stop();
21
+ const name = response?.portal?.name || portalId;
22
+ const links = response?.portal?.links || [];
23
+ if (links.length === 0) {
24
+ logger.dim(`Portal "${name}" has no links. Nothing to remove.`);
25
+ if (isJsonMode())
26
+ printJson({ portalId, cleared: true, removed: 0 });
27
+ return;
28
+ }
29
+ const ok = await confirmAction(`Remove all ${links.length} link(s) from portal "${name}"?`, options);
30
+ if (!ok) {
31
+ logger.dim('Aborted.');
32
+ process.exit(1);
33
+ }
34
+ const clearing = createSpinner('Removing links...').start();
19
35
  await portal.clearLinks(portalId);
20
- logger.success('Links removed successfully');
36
+ clearing.succeed(`Removed ${links.length} link(s) from portal "${name}"`);
37
+ if (isJsonMode())
38
+ printJson({ portalId, cleared: true, removed: links.length });
21
39
  }
22
40
  catch (error) {
23
- logger.error(error.message);
24
- process.exit(1);
41
+ fail(error);
25
42
  }
26
43
  });
27
44
  addExamples(clearLinksCommand, [
28
- { description: 'Remove every link from a portal',
29
- command: 'plazbot portal clear-links prt_AbcDef123' },
45
+ { description: 'Remove every link (asks for confirmation)', command: 'plazbot portal clear-links por_AbcDef123' },
46
+ { description: 'Remove every link without confirmation', command: 'plazbot portal clear-links por_AbcDef123 --yes' },
47
+ ]);
48
+ addNotes(clearLinksCommand, [
49
+ 'There is no command to remove a single link: clear them and add back the ones you want with add-link.',
30
50
  ]);
@@ -1,51 +1,80 @@
1
1
  import { Command } from 'commander';
2
- import { Portal } from 'plazbot';
3
- import { getStoredCredentials } from '../../utils/credentials.js';
2
+ import chalk from 'chalk';
3
+ import { addExamples, addNotes } from '../../utils/help.js';
4
+ import { jsonOption, isJsonMode, printJson, fail, CliError } from '../../utils/output.js';
5
+ import { createSpinner } from '../../utils/ui.js';
4
6
  import { logger } from '../../utils/logger.js';
5
- import { addExamples } from '../../utils/help.js';
7
+ import { createPortalClient, validateAccess, validateLogo } from './shared.js';
6
8
  export const createCommand = new Command('create')
7
- .description('Create a new portal with a simplified configuration')
9
+ .description('Create the portal of the active workspace')
8
10
  .requiredOption('-n, --name <name>', 'Portal name')
9
- .requiredOption('-t, --title <title>', 'Portal welcome title')
11
+ .requiredOption('-t, --title <title>', 'Welcome title shown on the portal')
12
+ .option('-s, --subtitle <subtitle>', 'Subtitle shown under the title')
13
+ .option('-l, --logo <url>', 'Logo URL (.png or .jpg)')
14
+ .option('-d, --logo-dark <url>', 'Dark-mode logo URL (.png or .jpg)')
15
+ .option('-a, --access <type>', 'Access type: direct (open) or form (asks for contact details first)', 'direct')
16
+ .addOption(jsonOption())
10
17
  .option('--dev', 'Use development environment', false)
11
18
  .action(async (options) => {
12
19
  try {
13
- // Obtener credenciales guardadas
14
- const credentials = await getStoredCredentials();
15
- const portal = new Portal({
16
- workspaceId: credentials.workspace,
17
- apiKey: credentials.apiKey,
18
- zone: credentials.zone,
19
- ...(options.dev && { customUrl: "http://localhost:5090" })
20
- });
21
- const portalCreated = await portal.addPortal({
22
- name: options.name,
23
- title: options.title,
24
- zone: credentials.zone,
25
- subtitle: "",
26
- logo: "",
27
- logodark: "",
28
- access: "direct",
29
- disabled: false,
30
- brandOff: false
31
- });
32
- logger.success('Portal created successfully');
33
- logger.title('Portal details');
34
- logger.label('ID', portalCreated.id);
35
- logger.label('URL', portalCreated.url);
36
- if (options.dev) {
37
- logger.warning('\nEnvironment: development');
20
+ const name = options.name.trim();
21
+ const title = options.title.trim();
22
+ if (!name)
23
+ throw new CliError('Portal name is required.', 'missing_name', 'Pass it with -n, --name <name>.');
24
+ if (!title)
25
+ throw new CliError('Portal title is required.', 'missing_title', 'Pass it with -t, --title <title>.');
26
+ const access = validateAccess(options.access || 'direct');
27
+ const logo = options.logo ? validateLogo(options.logo, '--logo') : undefined;
28
+ const logodark = options.logoDark ? validateLogo(options.logoDark, '--logo-dark') : undefined;
29
+ const { portal, credentials } = await createPortalClient(options.dev);
30
+ const spinner = createSpinner('Creating portal...').start();
31
+ let created;
32
+ try {
33
+ created = await portal.addPortal({
34
+ name,
35
+ title,
36
+ zone: credentials.zone,
37
+ subtitle: options.subtitle?.trim() || '',
38
+ ...(logo && { logo }),
39
+ ...(logodark && { logodark }),
40
+ access,
41
+ disabled: false,
42
+ brandOff: false,
43
+ });
38
44
  }
45
+ catch (error) {
46
+ spinner.stop();
47
+ if (error instanceof Error && /only one portal/i.test(error.message)) {
48
+ throw new CliError('This workspace already has a portal (only one is allowed).', 'portal_exists', "See it with 'plazbot portal list' or change it with 'plazbot portal update <portalId>'.");
49
+ }
50
+ throw error;
51
+ }
52
+ spinner.succeed(`Portal "${name}" created`);
53
+ if (isJsonMode()) {
54
+ printJson({ id: created.id, url: created.url });
55
+ return;
56
+ }
57
+ logger.label('ID', created.id);
58
+ logger.label('URL', created.url);
59
+ console.log();
60
+ logger.dim(`Next: ${chalk.cyan(`plazbot portal add-agent ${created.id} -a <agentId>`)}`);
61
+ console.log();
39
62
  }
40
63
  catch (error) {
41
- const message = error instanceof Error ? error.message : 'Unknown error while creating the portal';
42
- logger.error(message);
43
- process.exit(1);
64
+ fail(error);
44
65
  }
45
66
  });
46
67
  addExamples(createCommand, [
47
- { description: 'Create a portal with name and welcome title',
48
- command: 'plazbot portal create -n "Customer Portal" -t "Welcome to our help center"' },
49
- { description: 'Create against the local backend',
50
- command: 'plazbot portal create -n "Test" -t "Hello" --dev' },
68
+ { description: 'Create a portal with a name and welcome title',
69
+ command: 'plazbot portal create -n "Help Center" -t "How can we help you?"' },
70
+ { description: 'Create a portal with subtitle and logos',
71
+ command: 'plazbot portal create -n "Help Center" -t "Welcome" -s "Ask our AI agents" -l https://files.example.com/logo.png -d https://files.example.com/logo-dark.png' },
72
+ { description: 'Ask visitors for their details before chatting',
73
+ command: 'plazbot portal create -n "Support" -t "Welcome" -a form' },
74
+ { description: 'Create and capture the ID for scripts',
75
+ command: "plazbot portal create -n \"Help Center\" -t \"Welcome\" --json | jq -r '.id'" },
76
+ ]);
77
+ addNotes(createCommand, [
78
+ 'Only one portal per workspace is allowed.',
79
+ 'Logos must be .png or .jpg, given as an https:// URL or a path starting with "/".',
51
80
  ]);
@@ -1,58 +1,53 @@
1
1
  import { Command } from 'commander';
2
- import { Portal } from 'plazbot';
3
- import { getStoredCredentials } from '../../utils/credentials.js';
2
+ import { addExamples, addNotes } from '../../utils/help.js';
3
+ import { jsonOption, isJsonMode, printJson, fail } from '../../utils/output.js';
4
+ import { addYesOption, confirmAction } from '../../utils/confirm.js';
5
+ import { createSpinner } from '../../utils/ui.js';
4
6
  import { logger } from '../../utils/logger.js';
5
- import { addExamples } from '../../utils/help.js';
6
- import inquirer from 'inquirer';
7
+ import { createPortalClient, resolvePortalId } from './shared.js';
7
8
  export const deleteCommand = new Command('delete')
8
9
  .description('Delete a portal')
9
- .argument('<portalId>', 'Portal ID')
10
- .option('--force', 'Delete without confirmation', false)
11
- .option('--dev', 'Use development environment', false)
12
- .action(async (portalId, options) => {
10
+ .argument('<portalId>', 'Portal ID (see plazbot portal list)')
11
+ .addOption(jsonOption())
12
+ .option('--dev', 'Use development environment', false);
13
+ addYesOption(deleteCommand);
14
+ deleteCommand.action(async (portalIdArg, options) => {
13
15
  try {
14
- // Obtener credenciales guardadas
15
- const credentials = await getStoredCredentials();
16
- const portal = new Portal({
17
- workspaceId: credentials.workspace,
18
- apiKey: credentials.apiKey,
19
- zone: credentials.zone,
20
- ...(options.dev && { customUrl: "http://localhost:5090" })
21
- });
22
- // Obtener detalles del portal para mostrar qué se va a eliminar
23
- const portalDetails = await portal.getPortal(portalId);
24
- logger.title('Portal to delete');
25
- logger.label('ID', portalDetails.portal.id);
26
- logger.label('Name', portalDetails.portal.name);
27
- logger.label('URL', portalDetails.portal.url);
28
- if (!options.force) {
29
- const { confirm } = await inquirer.prompt([{
30
- type: 'confirm',
31
- name: 'confirm',
32
- message: 'Are you sure you want to delete this portal?',
33
- default: false
34
- }]);
35
- if (!confirm) {
36
- logger.info('Operation cancelled.');
37
- return;
38
- }
16
+ const portalId = resolvePortalId(portalIdArg);
17
+ const { portal } = await createPortalClient(options.dev);
18
+ const spinner = createSpinner('Loading portal...').start();
19
+ const response = (await portal.getPortal(portalId));
20
+ spinner.stop();
21
+ const details = response.portal;
22
+ const name = details?.name || portalId;
23
+ if (!isJsonMode()) {
24
+ logger.title('Portal to delete');
25
+ logger.label('ID', details?.id || portalId);
26
+ logger.label('Name', name);
27
+ if (details?.url)
28
+ logger.label('URL', details.url);
29
+ console.log();
39
30
  }
40
- logger.dim('Deleting portal...');
41
- await portal.deletePortal(portalId);
42
- logger.success('Portal deleted successfully');
43
- if (options.dev) {
44
- logger.warning('\nEnvironment: development');
31
+ const ok = await confirmAction(`Delete portal "${name}"? Its public URL will stop working.`, options);
32
+ if (!ok) {
33
+ logger.dim('Aborted.');
34
+ process.exit(1);
45
35
  }
36
+ const deleting = createSpinner('Deleting portal...').start();
37
+ await portal.deletePortal(portalId);
38
+ deleting.succeed(`Portal "${name}" deleted`);
39
+ if (isJsonMode())
40
+ printJson({ id: portalId, deleted: true });
46
41
  }
47
42
  catch (error) {
48
- const message = error instanceof Error ? error.message : 'Unknown error while deleting the portal';
49
- logger.error(message);
50
- process.exit(1);
43
+ fail(error);
51
44
  }
52
45
  });
53
46
  addExamples(deleteCommand, [
54
- { description: 'Delete a portal with confirmation prompt',
55
- command: 'plazbot portal delete prt_AbcDef123' },
56
- { description: 'Delete without confirmation (use in scripts)',
57
- command: 'plazbot portal delete prt_AbcDef123 --force' },
47
+ { description: 'Delete a portal (asks for confirmation)', command: 'plazbot portal delete por_AbcDef123' },
48
+ { description: 'Delete without confirmation (scripts and CI)', command: 'plazbot portal delete por_AbcDef123 --yes' },
49
+ { description: 'Delete and get the result as JSON', command: 'plazbot portal delete por_AbcDef123 --yes --json' },
50
+ ]);
51
+ addNotes(deleteCommand, [
52
+ 'This cannot be undone. Agents attached to the portal are not deleted.',
58
53
  ]);
@@ -1,66 +1,33 @@
1
1
  import { Command } from 'commander';
2
- import { Portal } from 'plazbot';
3
- import { getStoredCredentials } from '../../utils/credentials.js';
4
- import { logger } from '../../utils/logger.js';
5
2
  import { addExamples } from '../../utils/help.js';
3
+ import { jsonOption, isJsonMode, printJson, fail } from '../../utils/output.js';
4
+ import { createSpinner } from '../../utils/ui.js';
5
+ import { createPortalClient, printPortalDetails, resolvePortalId } from './shared.js';
6
6
  export const getCommand = new Command('get')
7
- .description('Show detailed information for a portal')
8
- .argument('<portalId>', 'Portal ID')
7
+ .description('Show the details of a portal (settings, links and agents)')
8
+ .argument('<portalId>', 'Portal ID (see plazbot portal list)')
9
+ .addOption(jsonOption())
9
10
  .option('--dev', 'Use development environment', false)
10
- .action(async (portalId, options) => {
11
+ .action(async (portalIdArg, options) => {
11
12
  try {
12
- // Obtener credenciales guardadas
13
- const credentials = await getStoredCredentials();
14
- const portal = new Portal({
15
- workspaceId: credentials.workspace,
16
- apiKey: credentials.apiKey,
17
- zone: credentials.zone,
18
- ...(options.dev && { customUrl: "http://localhost:5090" })
19
- });
20
- const response = await portal.getPortal(portalId);
21
- const portalDetails = response.portal;
22
- logger.title('Portal details');
23
- logger.label('ID', portalDetails.id);
24
- logger.label('Name', portalDetails.name);
25
- logger.label('URL', portalDetails.url);
26
- logger.label('Access', portalDetails.access);
27
- logger.label('Status', portalDetails.disabled ? 'Disabled' : 'Enabled');
28
- if (portalDetails.title) {
29
- logger.label('Title', portalDetails.title);
30
- }
31
- if (portalDetails.subtitle) {
32
- logger.label('Subtitle', portalDetails.subtitle);
33
- }
34
- if (portalDetails.logo) {
35
- logger.title('Assets');
36
- logger.label('Logo', portalDetails.logo);
37
- if (portalDetails.logodark) {
38
- logger.label('Logo (dark mode)', portalDetails.logodark);
39
- }
40
- }
41
- if (portalDetails.links && portalDetails.links.length > 0) {
42
- logger.title('Links');
43
- portalDetails.links.forEach((link) => {
44
- logger.label(link.value, link.url);
45
- });
46
- }
47
- if (portalDetails.agents && portalDetails.agents.length > 0) {
48
- logger.title('Associated agents');
49
- portalDetails.agents.forEach((agent) => {
50
- logger.label(agent.name, agent.id);
51
- });
52
- }
53
- if (options.dev) {
54
- logger.warning('\nEnvironment: development');
13
+ const portalId = resolvePortalId(portalIdArg);
14
+ const { portal } = await createPortalClient(options.dev);
15
+ const spinner = createSpinner('Loading portal...').start();
16
+ const response = (await portal.getPortal(portalId));
17
+ spinner.stop();
18
+ if (isJsonMode()) {
19
+ printJson(response.portal);
20
+ return;
55
21
  }
22
+ printPortalDetails(response.portal, 'Portal details');
23
+ console.log();
56
24
  }
57
25
  catch (error) {
58
- const message = error instanceof Error ? error.message : 'Unknown error while fetching portal details';
59
- logger.error(message);
60
- process.exit(1);
26
+ fail(error);
61
27
  }
62
28
  });
63
29
  addExamples(getCommand, [
64
- { description: 'Show details for a specific portal',
65
- command: 'plazbot portal get prt_AbcDef123' },
30
+ { description: 'Show the details of a portal', command: 'plazbot portal get por_AbcDef123' },
31
+ { description: 'Get the portal as JSON', command: 'plazbot portal get por_AbcDef123 --json' },
32
+ { description: 'List the agents attached to the portal', command: "plazbot portal get por_AbcDef123 --json | jq '.agents'" },
66
33
  ]);