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 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 -->
@@ -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, '--content');
75
+ export async function readTextInput(value, flagName = '--content') {
76
+ return resolveInput(value, flagName);
77
77
  }
@@ -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', { apiKey: key });
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/credential', {
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
+ }