chatbase 0.1.0 → 0.2.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.
@@ -0,0 +1,94 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { AgentCommand } from '../../base/agent-command.js';
3
+ import { readTextInput } from '../../base/body-input.js';
4
+ import { throwIfError } from '../../client/client.js';
5
+ import { UsageError } from '../../errors/errors.js';
6
+ /** Parse --variables: a JSON object grouped by component, the exact shape
7
+ * `whatsapp templates` reports per template. Leaf validation is the API's
8
+ * job — it returns field-level errors for bad keys or non-string values. */
9
+ async function parseVariables(value) {
10
+ const raw = await readTextInput(value, '--variables');
11
+ let parsed;
12
+ try {
13
+ parsed = JSON.parse(raw);
14
+ }
15
+ catch {
16
+ throw new UsageError('--variables must be valid JSON (inline, @file, or @-).');
17
+ }
18
+ if (typeof parsed !== 'object' ||
19
+ parsed === null ||
20
+ Array.isArray(parsed)) {
21
+ throw new UsageError('--variables must be a JSON object grouped by component, e.g. \'{"body":{"1":"Jane"}}\'.');
22
+ }
23
+ return parsed;
24
+ }
25
+ export default class WhatsappSendTemplate extends AgentCommand {
26
+ static summary = 'Send an approved WhatsApp template message';
27
+ static description = 'Send an approved WhatsApp template to a phone number from one of the ' +
28
+ 'agent’s connected numbers. No user ID is needed — a Chatbase user is ' +
29
+ 'resolved or created from the recipient number, and replies flow ' +
30
+ 'through the agent’s regular WhatsApp pipeline. Use `whatsapp ' +
31
+ 'templates` to see available templates and the variables each expects.';
32
+ static examples = [
33
+ '<%= config.bin %> whatsapp send-template order_update --to 14155552671 -a agt_123',
34
+ '<%= config.bin %> whatsapp send-template order_update --to 14155552671 --language en_US --variables \'{"header":{"1":"#1042"},"body":{"1":"Jane","2":"Friday"}}\' -a agt_123'
35
+ ];
36
+ static args = {
37
+ template: Args.string({
38
+ required: true,
39
+ description: 'Name of the approved template'
40
+ })
41
+ };
42
+ static flags = {
43
+ ...AgentCommand.baseFlags,
44
+ to: Flags.string({
45
+ required: true,
46
+ description: 'Recipient phone number in international format (digits with country code)'
47
+ }),
48
+ from: Flags.string({
49
+ description: 'Which connected WhatsApp number to send from — optional when the agent has exactly one'
50
+ }),
51
+ language: Flags.string({
52
+ description: 'Template language code (e.g. en_US) — optional when the template exists in a single language'
53
+ }),
54
+ variables: Flags.string({
55
+ description: 'Template variable values as JSON grouped by component (@file, @-, or inline), e.g. \'{"body":{"1":"Jane"}}\''
56
+ })
57
+ };
58
+ async run() {
59
+ const { args, flags } = await this.parse(WhatsappSendTemplate);
60
+ const variables = flags.variables
61
+ ? await parseVariables(flags.variables)
62
+ : undefined;
63
+ const client = this.apiClient(flags);
64
+ const agentId = await this.agentId(flags, client);
65
+ const body = {
66
+ to: flags.to,
67
+ ...(flags.from ? { from: flags.from } : {}),
68
+ template: {
69
+ name: args.template,
70
+ ...(flags.language ? { language: flags.language } : {}),
71
+ // The spec defaults variables to {} and the generated type
72
+ // makes it required — send the default explicitly.
73
+ variables: variables ?? {}
74
+ }
75
+ };
76
+ const { data, error, response } = await client.POST('/agents/{agentId}/whatsapp/messages/template', {
77
+ params: { path: { agentId } },
78
+ body
79
+ });
80
+ throwIfError(response, error);
81
+ const sent = data;
82
+ if (flags.json) {
83
+ process.stdout.write(`${JSON.stringify(sent, null, 2)}\n`);
84
+ return;
85
+ }
86
+ this.success(flags, `Sent template "${args.template}" to ${sent.to}`);
87
+ if (sent.conversationId) {
88
+ this.note(flags, `Conversation: ${sent.conversationId}`);
89
+ }
90
+ if (sent.messageId) {
91
+ process.stdout.write(`${sent.messageId}\n`);
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,55 @@
1
+ import { AgentCommand } from '../../base/agent-command.js';
2
+ import { throwIfError } from '../../client/client.js';
3
+ const COLUMNS = [
4
+ { key: 'name', header: 'NAME' },
5
+ { key: 'language', header: 'LANGUAGE' },
6
+ { key: 'category', header: 'CATEGORY' },
7
+ { key: 'format', header: 'FORMAT' },
8
+ { key: 'wabaId', header: 'WABA' },
9
+ { key: 'variables', header: 'VARIABLES' }
10
+ ];
11
+ /** {header: ["1"], body: ["1","2"]} → "header:1 body:1,2" — the keys a send
12
+ * must supply, in the grouping `send-template --variables` expects. */
13
+ function variablesSummary(variables) {
14
+ return Object.entries(variables)
15
+ .map(([component, keys]) => `${component}:${keys.join(',')}`)
16
+ .join(' ');
17
+ }
18
+ export default class WhatsappTemplates extends AgentCommand {
19
+ static summary = 'List approved WhatsApp templates for an agent';
20
+ static description = 'List the approved WhatsApp templates available to the agent, across ' +
21
+ 'all of its connected WhatsApp Business Accounts. A template can only ' +
22
+ 'be sent from a number on its own Business Account — pick a sender ' +
23
+ 'whose WABA matches the template’s WABA column.';
24
+ static examples = [
25
+ '<%= config.bin %> whatsapp templates -a agt_123',
26
+ '<%= config.bin %> whatsapp templates -a agt_123 --json'
27
+ ];
28
+ static flags = { ...AgentCommand.baseFlags };
29
+ async run() {
30
+ const { flags } = await this.parse(WhatsappTemplates);
31
+ const client = this.apiClient(flags);
32
+ const agentId = await this.agentId(flags, client);
33
+ const { data, error, response } = await client.GET('/agents/{agentId}/whatsapp/templates', { params: { path: { agentId } } });
34
+ throwIfError(response, error);
35
+ const result = data;
36
+ const rows = result.templates.map((t) => ({
37
+ name: t.name,
38
+ language: t.language,
39
+ category: t.category,
40
+ format: t.parameterFormat,
41
+ wabaId: t.wabaId,
42
+ variables: variablesSummary(t.variables)
43
+ }));
44
+ this.printData(flags, result, rows, COLUMNS);
45
+ if (result.senders.length > 0) {
46
+ const senders = result.senders
47
+ .map((s) => `${s.from}${s.verifiedName ? ` (${s.verifiedName})` : ''} — waba ${s.wabaId}`)
48
+ .join('; ');
49
+ this.note(flags, `Senders: ${senders}`);
50
+ }
51
+ if (!result.complete) {
52
+ this.note(flags, `Warning: partial list — could not read WABA(s): ${result.unavailableWabaIds.join(', ')}. Retry to pick them up.`);
53
+ }
54
+ }
55
+ }