chatbase 0.1.0 → 0.1.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 +127 -0
- package/dist/base/body-input.js +2 -2
- package/dist/client/pairing.js +1 -2
- package/dist/commands/auth/login.js +5 -2
- package/dist/commands/auth/logout.js +4 -2
- package/dist/commands/auth/status.js +4 -2
- package/dist/commands/conversations/tool-result.js +74 -0
- package/dist/commands/whatsapp/send-template.js +94 -0
- package/dist/commands/whatsapp/templates.js +55 -0
- package/oclif.manifest.json +658 -342
- package/package.json +5 -1
- package/spec/openapi.json +37 -0
package/README.md
CHANGED
|
@@ -56,6 +56,7 @@ calls you invoke.
|
|
|
56
56
|
* [`chatbase conversations export`](#chatbase-conversations-export)
|
|
57
57
|
* [`chatbase conversations get [CONVERSATIONID]`](#chatbase-conversations-get-conversationid)
|
|
58
58
|
* [`chatbase conversations list`](#chatbase-conversations-list)
|
|
59
|
+
* [`chatbase conversations tool-result [CONVERSATIONID]`](#chatbase-conversations-tool-result-conversationid)
|
|
59
60
|
* [`chatbase health`](#chatbase-health)
|
|
60
61
|
* [`chatbase help [COMMAND]`](#chatbase-help-command)
|
|
61
62
|
* [`chatbase helpdesk statuses`](#chatbase-helpdesk-statuses)
|
|
@@ -76,6 +77,8 @@ calls you invoke.
|
|
|
76
77
|
* [`chatbase tickets reply [TICKETNUMBER]`](#chatbase-tickets-reply-ticketnumber)
|
|
77
78
|
* [`chatbase tickets search QUERY`](#chatbase-tickets-search-query)
|
|
78
79
|
* [`chatbase tickets update TICKETNUMBER`](#chatbase-tickets-update-ticketnumber)
|
|
80
|
+
* [`chatbase whatsapp send-template TEMPLATE`](#chatbase-whatsapp-send-template-template)
|
|
81
|
+
* [`chatbase whatsapp templates`](#chatbase-whatsapp-templates)
|
|
79
82
|
|
|
80
83
|
## `chatbase agents auto-retrain [AGENTID]`
|
|
81
84
|
|
|
@@ -817,6 +820,50 @@ EXAMPLES
|
|
|
817
820
|
|
|
818
821
|
_See code: [src/commands/conversations/list.ts](https://github.com/Chatbase-co/chatbase-cli/blob/v0.1.0/src/commands/conversations/list.ts)_
|
|
819
822
|
|
|
823
|
+
## `chatbase conversations tool-result [CONVERSATIONID]`
|
|
824
|
+
|
|
825
|
+
Submit a client-side tool result to a chat turn
|
|
826
|
+
|
|
827
|
+
```
|
|
828
|
+
USAGE
|
|
829
|
+
$ chatbase conversations tool-result [CONVERSATIONID] --tool-call-id <value> [--json] [--plain] [-q] [--verbose] [--no-input]
|
|
830
|
+
[--no-color] [--agent-name <value> | -a <value>] [--conversation <value>] [--output <value>]
|
|
831
|
+
|
|
832
|
+
ARGUMENTS
|
|
833
|
+
[CONVERSATIONID] Conversation ID (alternative to --conversation)
|
|
834
|
+
|
|
835
|
+
FLAGS
|
|
836
|
+
-a, --agent=<value> Agent ID (or set CHATBASE_AGENT_ID)
|
|
837
|
+
-q, --quiet Suppress non-essential output
|
|
838
|
+
--agent-name=<value> Agent display name (looked up to an ID)
|
|
839
|
+
--conversation=<value> Conversation ID
|
|
840
|
+
--no-color Disable colored output
|
|
841
|
+
--no-input Never prompt; fail instead
|
|
842
|
+
--output=<value> Result of executing the tool (inline JSON, @file, or @-); a value that is not valid JSON
|
|
843
|
+
is sent as a plain string
|
|
844
|
+
--tool-call-id=<value> (required) The toolCallId from the tool-call part in the chat response
|
|
845
|
+
--verbose Verbose diagnostics
|
|
846
|
+
|
|
847
|
+
OUTPUT FLAGS
|
|
848
|
+
--json Output raw API JSON
|
|
849
|
+
--plain Tab-separated output for scripts
|
|
850
|
+
|
|
851
|
+
DESCRIPTION
|
|
852
|
+
Submit a client-side tool result to a chat turn
|
|
853
|
+
|
|
854
|
+
Submit the result of a client-side tool call so the paused chat turn can continue. The tool call ID comes from the
|
|
855
|
+
tool-call part of the chat response that requested the execution.
|
|
856
|
+
|
|
857
|
+
EXAMPLES
|
|
858
|
+
$ chatbase conversations tool-result conv_123 --tool-call-id tc_1 --output '{"temperature": 72}' -a agt_123
|
|
859
|
+
|
|
860
|
+
$ chatbase conversations tool-result conv_123 --tool-call-id tc_1 --output @result.json -a agt_123
|
|
861
|
+
|
|
862
|
+
$ chatbase conversations tool-result conv_123 --tool-call-id tc_1 -a agt_123
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
_See code: [src/commands/conversations/tool-result.ts](https://github.com/Chatbase-co/chatbase-cli/blob/v0.1.0/src/commands/conversations/tool-result.ts)_
|
|
866
|
+
|
|
820
867
|
## `chatbase health`
|
|
821
868
|
|
|
822
869
|
Check that the Chatbase API is reachable
|
|
@@ -1526,4 +1573,84 @@ EXAMPLES
|
|
|
1526
1573
|
```
|
|
1527
1574
|
|
|
1528
1575
|
_See code: [src/commands/tickets/update.ts](https://github.com/Chatbase-co/chatbase-cli/blob/v0.1.0/src/commands/tickets/update.ts)_
|
|
1576
|
+
|
|
1577
|
+
## `chatbase whatsapp send-template TEMPLATE`
|
|
1578
|
+
|
|
1579
|
+
Send an approved WhatsApp template message
|
|
1580
|
+
|
|
1581
|
+
```
|
|
1582
|
+
USAGE
|
|
1583
|
+
$ chatbase whatsapp send-template TEMPLATE --to <value> [--json] [--plain] [-q] [--verbose] [--no-input] [--no-color]
|
|
1584
|
+
[--agent-name <value> | -a <value>] [--from <value>] [--language <value>] [--variables <value>]
|
|
1585
|
+
|
|
1586
|
+
ARGUMENTS
|
|
1587
|
+
TEMPLATE Name of the approved template
|
|
1588
|
+
|
|
1589
|
+
FLAGS
|
|
1590
|
+
-a, --agent=<value> Agent ID (or set CHATBASE_AGENT_ID)
|
|
1591
|
+
-q, --quiet Suppress non-essential output
|
|
1592
|
+
--agent-name=<value> Agent display name (looked up to an ID)
|
|
1593
|
+
--from=<value> Which connected WhatsApp number to send from — optional when the agent has exactly one
|
|
1594
|
+
--language=<value> Template language code (e.g. en_US) — optional when the template exists in a single language
|
|
1595
|
+
--no-color Disable colored output
|
|
1596
|
+
--no-input Never prompt; fail instead
|
|
1597
|
+
--to=<value> (required) Recipient phone number in international format (digits with country code)
|
|
1598
|
+
--variables=<value> Template variable values as JSON grouped by component (@file, @-, or inline), e.g.
|
|
1599
|
+
'{"body":{"1":"Jane"}}'
|
|
1600
|
+
--verbose Verbose diagnostics
|
|
1601
|
+
|
|
1602
|
+
OUTPUT FLAGS
|
|
1603
|
+
--json Output raw API JSON
|
|
1604
|
+
--plain Tab-separated output for scripts
|
|
1605
|
+
|
|
1606
|
+
DESCRIPTION
|
|
1607
|
+
Send an approved WhatsApp template message
|
|
1608
|
+
|
|
1609
|
+
Send an approved WhatsApp template to a phone number from one of the agent’s connected numbers. No user ID is needed —
|
|
1610
|
+
a Chatbase user is resolved or created from the recipient number, and replies flow through the agent’s regular
|
|
1611
|
+
WhatsApp pipeline. Use `whatsapp templates` to see available templates and the variables each expects.
|
|
1612
|
+
|
|
1613
|
+
EXAMPLES
|
|
1614
|
+
$ chatbase whatsapp send-template order_update --to 14155552671 -a agt_123
|
|
1615
|
+
|
|
1616
|
+
$ chatbase whatsapp send-template order_update --to 14155552671 --language en_US --variables '{"header":{"1":"#1042"},"body":{"1":"Jane","2":"Friday"}}' -a agt_123
|
|
1617
|
+
```
|
|
1618
|
+
|
|
1619
|
+
_See code: [src/commands/whatsapp/send-template.ts](https://github.com/Chatbase-co/chatbase-cli/blob/v0.1.0/src/commands/whatsapp/send-template.ts)_
|
|
1620
|
+
|
|
1621
|
+
## `chatbase whatsapp templates`
|
|
1622
|
+
|
|
1623
|
+
List approved WhatsApp templates for an agent
|
|
1624
|
+
|
|
1625
|
+
```
|
|
1626
|
+
USAGE
|
|
1627
|
+
$ chatbase whatsapp templates [--json] [--plain] [-q] [--verbose] [--no-input] [--no-color] [--agent-name <value> | -a
|
|
1628
|
+
<value>]
|
|
1629
|
+
|
|
1630
|
+
FLAGS
|
|
1631
|
+
-a, --agent=<value> Agent ID (or set CHATBASE_AGENT_ID)
|
|
1632
|
+
-q, --quiet Suppress non-essential output
|
|
1633
|
+
--agent-name=<value> Agent display name (looked up to an ID)
|
|
1634
|
+
--no-color Disable colored output
|
|
1635
|
+
--no-input Never prompt; fail instead
|
|
1636
|
+
--verbose Verbose diagnostics
|
|
1637
|
+
|
|
1638
|
+
OUTPUT FLAGS
|
|
1639
|
+
--json Output raw API JSON
|
|
1640
|
+
--plain Tab-separated output for scripts
|
|
1641
|
+
|
|
1642
|
+
DESCRIPTION
|
|
1643
|
+
List approved WhatsApp templates for an agent
|
|
1644
|
+
|
|
1645
|
+
List the approved WhatsApp templates available to the agent, across all of its connected WhatsApp Business Accounts. A
|
|
1646
|
+
template can only be sent from a number on its own Business Account — pick a sender whose WABA matches the template’s
|
|
1647
|
+
WABA column.
|
|
1648
|
+
|
|
1649
|
+
EXAMPLES
|
|
1650
|
+
$ chatbase whatsapp templates -a agt_123
|
|
1651
|
+
|
|
1652
|
+
$ chatbase whatsapp templates -a agt_123 --json
|
|
1653
|
+
```
|
|
1654
|
+
|
|
1655
|
+
_See code: [src/commands/whatsapp/templates.ts](https://github.com/Chatbase-co/chatbase-cli/blob/v0.1.0/src/commands/whatsapp/templates.ts)_
|
|
1529
1656
|
<!-- commandsstop -->
|
package/dist/base/body-input.js
CHANGED
|
@@ -72,6 +72,6 @@ export async function readBodyData(data, fields) {
|
|
|
72
72
|
return { ...base, ...parseFields(fields) };
|
|
73
73
|
}
|
|
74
74
|
/** Resolve a flag value via the same @file/@- indirection, without JSON parsing. */
|
|
75
|
-
export async function readTextInput(value) {
|
|
76
|
-
return resolveInput(value,
|
|
75
|
+
export async function readTextInput(value, flagName = '--content') {
|
|
76
|
+
return resolveInput(value, flagName);
|
|
77
77
|
}
|
package/dist/client/pairing.js
CHANGED
|
@@ -5,13 +5,12 @@
|
|
|
5
5
|
* 3. Poll POST /api/cli-pairing/exchange until approved
|
|
6
6
|
* 4. Receive the minted API key + workspace info
|
|
7
7
|
*
|
|
8
|
-
* These endpoints are internal (not under /api/v2) and unauthenticated.
|
|
9
8
|
*/
|
|
10
9
|
import os from 'node:os';
|
|
11
10
|
import { parseErrorResponse, UsageError } from '../errors/errors.js';
|
|
12
11
|
import { rawApiFetch, resolveBaseUrl } from './client.js';
|
|
13
12
|
import { wasInterrupted } from './signals.js';
|
|
14
|
-
function pairingBaseUrl(baseUrl) {
|
|
13
|
+
export function pairingBaseUrl(baseUrl) {
|
|
15
14
|
return new URL(resolveBaseUrl(baseUrl)).origin;
|
|
16
15
|
}
|
|
17
16
|
export async function startPairing(opts) {
|
|
@@ -3,7 +3,7 @@ import { Flags } from '@oclif/core';
|
|
|
3
3
|
import { BaseCommand } from '../../base/base-command.js';
|
|
4
4
|
import { readStdinToEnd } from '../../base/body-input.js';
|
|
5
5
|
import { rawApiFetch } from '../../client/client.js';
|
|
6
|
-
import { pollExchange, startPairing } from '../../client/pairing.js';
|
|
6
|
+
import { pairingBaseUrl, pollExchange, startPairing } from '../../client/pairing.js';
|
|
7
7
|
import { configFile } from '../../config/paths.js';
|
|
8
8
|
import { writeUserConfig } from '../../config/store.js';
|
|
9
9
|
import { parseErrorResponse, UsageError } from '../../errors/errors.js';
|
|
@@ -107,7 +107,10 @@ export default class AuthLogin extends BaseCommand {
|
|
|
107
107
|
this.note(flags, `Saved to ${configFile()}`);
|
|
108
108
|
}
|
|
109
109
|
async verifyAndStore(flags, key) {
|
|
110
|
-
const res = await rawApiFetch('GET', '/me', {
|
|
110
|
+
const res = await rawApiFetch('GET', '/api/cli-pairing/me', {
|
|
111
|
+
apiKey: key,
|
|
112
|
+
baseUrl: pairingBaseUrl()
|
|
113
|
+
});
|
|
111
114
|
if (res.status === 200) {
|
|
112
115
|
const body = res.body;
|
|
113
116
|
writeUserConfig({ apiKey: key });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { BaseCommand } from '../../base/base-command.js';
|
|
3
3
|
import { rawApiFetch } from '../../client/client.js';
|
|
4
|
+
import { pairingBaseUrl } from '../../client/pairing.js';
|
|
4
5
|
import { configFile } from '../../config/paths.js';
|
|
5
6
|
import { readUserConfig } from '../../config/store.js';
|
|
6
7
|
export default class AuthLogout extends BaseCommand {
|
|
@@ -17,8 +18,9 @@ export default class AuthLogout extends BaseCommand {
|
|
|
17
18
|
}
|
|
18
19
|
if (config.apiKeySource === 'pairing') {
|
|
19
20
|
try {
|
|
20
|
-
const res = await rawApiFetch('DELETE', '/me
|
|
21
|
-
apiKey: config.apiKey
|
|
21
|
+
const res = await rawApiFetch('DELETE', '/api/cli-pairing/me', {
|
|
22
|
+
apiKey: config.apiKey,
|
|
23
|
+
baseUrl: pairingBaseUrl()
|
|
22
24
|
});
|
|
23
25
|
if (res.status >= 200 && res.status < 300) {
|
|
24
26
|
this.note(flags, 'CLI session revoked server-side.');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base/base-command.js';
|
|
2
2
|
import { DEFAULT_BASE_URL, rawApiFetch, resolveBaseUrl } from '../../client/client.js';
|
|
3
|
+
import { pairingBaseUrl } from '../../client/pairing.js';
|
|
3
4
|
import { resolveApiKey } from '../../config/resolve.js';
|
|
4
5
|
export default class AuthStatus extends BaseCommand {
|
|
5
6
|
static description = 'Show the active credential and where it comes from';
|
|
@@ -18,8 +19,9 @@ export default class AuthStatus extends BaseCommand {
|
|
|
18
19
|
}
|
|
19
20
|
const tail = resolved.value.length > 8 ? `…${resolved.value.slice(-4)}` : '…****';
|
|
20
21
|
this.note(flags, `Credential: ${tail} (from ${resolved.source})`);
|
|
21
|
-
const res = await rawApiFetch('GET', '/me', {
|
|
22
|
-
apiKey: resolved.value
|
|
22
|
+
const res = await rawApiFetch('GET', '/api/cli-pairing/me', {
|
|
23
|
+
apiKey: resolved.value,
|
|
24
|
+
baseUrl: pairingBaseUrl()
|
|
23
25
|
});
|
|
24
26
|
if (res.status === 200) {
|
|
25
27
|
this.renderMe(flags, res.body);
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|
+
export default class ConversationsToolResult extends AgentCommand {
|
|
7
|
+
static summary = 'Submit a client-side tool result to a chat turn';
|
|
8
|
+
static description = 'Submit the result of a client-side tool call so the paused chat ' +
|
|
9
|
+
'turn can continue. The tool call ID comes from the tool-call part ' +
|
|
10
|
+
'of the chat response that requested the execution.';
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> conversations tool-result conv_123 --tool-call-id tc_1 --output \'{"temperature": 72}\' -a agt_123',
|
|
13
|
+
'<%= config.bin %> conversations tool-result conv_123 --tool-call-id tc_1 --output @result.json -a agt_123',
|
|
14
|
+
'<%= config.bin %> conversations tool-result conv_123 --tool-call-id tc_1 -a agt_123'
|
|
15
|
+
];
|
|
16
|
+
static args = {
|
|
17
|
+
conversationId: Args.string({
|
|
18
|
+
required: false,
|
|
19
|
+
description: 'Conversation ID (alternative to --conversation)'
|
|
20
|
+
})
|
|
21
|
+
};
|
|
22
|
+
static flags = {
|
|
23
|
+
...AgentCommand.baseFlags,
|
|
24
|
+
conversation: Flags.string({
|
|
25
|
+
description: 'Conversation ID'
|
|
26
|
+
}),
|
|
27
|
+
'tool-call-id': Flags.string({
|
|
28
|
+
required: true,
|
|
29
|
+
description: 'The toolCallId from the tool-call part in the chat response'
|
|
30
|
+
}),
|
|
31
|
+
output: Flags.string({
|
|
32
|
+
description: 'Result of executing the tool (inline JSON, @file, or @-); ' +
|
|
33
|
+
'a value that is not valid JSON is sent as a plain string'
|
|
34
|
+
})
|
|
35
|
+
};
|
|
36
|
+
async run() {
|
|
37
|
+
const { args, flags } = await this.parse(ConversationsToolResult);
|
|
38
|
+
// Positional and flag are alternatives — `agents get <id>` set the
|
|
39
|
+
// positional convention, the flag predates it and stays supported.
|
|
40
|
+
const conversationId = args.conversationId ?? flags.conversation;
|
|
41
|
+
if (!conversationId) {
|
|
42
|
+
throw new UsageError('Missing conversation ID. Pass it positionally (`conversations tool-result <id> ...`) or via --conversation.');
|
|
43
|
+
}
|
|
44
|
+
if (args.conversationId && flags.conversation) {
|
|
45
|
+
throw new UsageError('Pass the conversation ID either positionally or via --conversation, not both.');
|
|
46
|
+
}
|
|
47
|
+
const body = {
|
|
48
|
+
toolCallId: flags['tool-call-id']
|
|
49
|
+
};
|
|
50
|
+
if (flags.output !== undefined) {
|
|
51
|
+
const raw = await readTextInput(flags.output, '--output');
|
|
52
|
+
// Tool outputs are arbitrary values in the spec: parse JSON when
|
|
53
|
+
// it is JSON, otherwise pass the text through as a string.
|
|
54
|
+
try {
|
|
55
|
+
body.output = JSON.parse(raw);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
body.output = raw;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const client = this.apiClient(flags);
|
|
62
|
+
const agentId = await this.agentId(flags, client);
|
|
63
|
+
const { data, error, response } = await client.POST('/agents/{agentId}/conversations/{conversationId}/tool-result', {
|
|
64
|
+
params: { path: { agentId, conversationId } },
|
|
65
|
+
body
|
|
66
|
+
});
|
|
67
|
+
throwIfError(response, error);
|
|
68
|
+
if (flags.json) {
|
|
69
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.success(flags, `Tool result submitted for ${flags['tool-call-id']}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -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
|
+
}
|