callkaro-cli-unofficial 0.8.0 → 0.9.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,374 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.inboxListCommand = inboxListCommand;
4
+ exports.inboxUnreadCommand = inboxUnreadCommand;
5
+ exports.inboxHandoffsCommand = inboxHandoffsCommand;
6
+ exports.inboxReleaseHandoffCommand = inboxReleaseHandoffCommand;
7
+ exports.inboxContactCommand = inboxContactCommand;
8
+ exports.inboxClearCommand = inboxClearCommand;
9
+ exports.inboxDeassignCommand = inboxDeassignCommand;
10
+ exports.waCampaignsListCommand = waCampaignsListCommand;
11
+ exports.waCampaignAnalyticsCommand = waCampaignAnalyticsCommand;
12
+ exports.waCampaignTimeseriesCommand = waCampaignTimeseriesCommand;
13
+ exports.waCampaignExportCommand = waCampaignExportCommand;
14
+ exports.waCampaignSendCommand = waCampaignSendCommand;
15
+ exports.finetuneListCommand = finetuneListCommand;
16
+ exports.finetuneBucketsCommand = finetuneBucketsCommand;
17
+ exports.finetuneCreateBucketCommand = finetuneCreateBucketCommand;
18
+ exports.finetuneJobsCommand = finetuneJobsCommand;
19
+ exports.finetuneStatusCommand = finetuneStatusCommand;
20
+ exports.finetuneStartCommand = finetuneStartCommand;
21
+ exports.finetuneAddModelCommand = finetuneAddModelCommand;
22
+ exports.finetuneCancelCommand = finetuneCancelCommand;
23
+ exports.finetuneDeleteRecordCommand = finetuneDeleteRecordCommand;
24
+ exports.teamMembersCommand = teamMembersCommand;
25
+ exports.teamRolesCommand = teamRolesCommand;
26
+ exports.teamCreateRoleCommand = teamCreateRoleCommand;
27
+ exports.teamAddMemberCommand = teamAddMemberCommand;
28
+ exports.teamUpdateMemberCommand = teamUpdateMemberCommand;
29
+ exports.teamRemoveMemberCommand = teamRemoveMemberCommand;
30
+ exports.teamSetWebhookCommand = teamSetWebhookCommand;
31
+ exports.complianceListCommand = complianceListCommand;
32
+ exports.complianceCreateCommand = complianceCreateCommand;
33
+ exports.complianceDeleteCommand = complianceDeleteCommand;
34
+ exports.verifiedNumbersListCommand = verifiedNumbersListCommand;
35
+ exports.verifiedNumbersAddCommand = verifiedNumbersAddCommand;
36
+ exports.verifiedNumbersVerifyCommand = verifiedNumbersVerifyCommand;
37
+ exports.verifiedNumbersDeleteCommand = verifiedNumbersDeleteCommand;
38
+ exports.integrationsListCommand = integrationsListCommand;
39
+ exports.integrationsCreateCommand = integrationsCreateCommand;
40
+ exports.integrationsUpdateCommand = integrationsUpdateCommand;
41
+ exports.integrationsDeleteCommand = integrationsDeleteCommand;
42
+ exports.integrationsSipCommand = integrationsSipCommand;
43
+ exports.integrationsAisensyCommand = integrationsAisensyCommand;
44
+ exports.integrationsHubspotCommand = integrationsHubspotCommand;
45
+ exports.integrationsShopifyCommand = integrationsShopifyCommand;
46
+ const fs_1 = require("fs");
47
+ const dashboardApi_1 = require("../lib/dashboardApi");
48
+ const ui_1 = require("../lib/ui");
49
+ const setJson_1 = require("./setJson");
50
+ const splitList = (raw) => String(raw || '').split(',').map((s) => s.trim()).filter(Boolean);
51
+ const cell = (v) => v && typeof v === 'object' ? JSON.stringify(v) : v;
52
+ /** Run a call; `--json` prints it raw, otherwise `render` prints it. */
53
+ async function run(opts, call, render) {
54
+ try {
55
+ const res = await call();
56
+ if (opts.json)
57
+ return void console.log(JSON.stringify(res, null, 2));
58
+ render(res);
59
+ }
60
+ catch (err) {
61
+ (0, ui_1.handleError)(err);
62
+ }
63
+ }
64
+ /** List handler: pull `key` off the response, table the `cols`, or say `empty`. */
65
+ function list(key, empty, cols) {
66
+ return (res) => {
67
+ const rows = res?.[key] || [];
68
+ if (!rows.length)
69
+ return (0, ui_1.info)(empty);
70
+ (0, ui_1.table)(cols.map((c) => c[0]), rows.map((r) => cols.map((c) => cell(c[1](r)))));
71
+ };
72
+ }
73
+ const ok = (msg) => () => (0, ui_1.success)(msg);
74
+ const id = (r) => r._id ?? r.id;
75
+ /** Confirm, then run a destructive call. */
76
+ async function guarded(message, opts, call, done) {
77
+ try {
78
+ if (!(await (0, ui_1.confirm)(message, opts.yes)))
79
+ return;
80
+ await run(opts, call, ok(done));
81
+ }
82
+ catch (err) {
83
+ (0, ui_1.handleError)(err);
84
+ }
85
+ }
86
+ // --- Inbox ---------------------------------------------------------------------
87
+ async function inboxListCommand(opts) {
88
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().inbox({
89
+ page: opts.page, channel: opts.channel, search: opts.search,
90
+ agentPhoneNumber: opts.number, since: opts.since, until: opts.until,
91
+ }), (res) => {
92
+ const rows = res?.contacts || res?.threads || res?.inbox || res?.data || [];
93
+ if (!rows.length)
94
+ return (0, ui_1.info)('Inbox is empty.');
95
+ (0, ui_1.table)(['PHONE', 'CHANNEL', 'LAST MESSAGE', 'UNREAD'], rows.map((r) => [
96
+ r.phoneNumber ?? r.phone ?? r.contact, r.channel, cell(r.lastMessage ?? r.last_message), r.unreadCount ?? r.unread,
97
+ ]));
98
+ });
99
+ }
100
+ async function inboxUnreadCommand(opts) {
101
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().inboxUnreadContacts(), (res) => {
102
+ const rows = res?.unreadContacts || [];
103
+ if (!rows.length)
104
+ return (0, ui_1.info)('No unread contacts.');
105
+ (0, ui_1.table)(['CONTACT'], rows.map((r) => [typeof r === 'object' ? (r.phoneNumber ?? r.phone ?? JSON.stringify(r)) : r]));
106
+ });
107
+ }
108
+ async function inboxHandoffsCommand(opts) {
109
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().inboxHumanHandoffs(opts.channel), list('handoffs', 'No human handoffs.', [
110
+ ['FROM', (r) => r.from_id ?? r.fromId ?? r.phoneNumber], ['CHANNEL', (r) => r.channel], ['TO', (r) => r.to_id ?? r.toId],
111
+ ]));
112
+ }
113
+ async function inboxReleaseHandoffCommand(fromId, opts) {
114
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().inboxReleaseHandoff({ channel: opts.channel, from_id: fromId, to_id: opts.toId }), ok(`Released handoff for ${fromId}.`));
115
+ }
116
+ async function inboxContactCommand(phone, opts) {
117
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().inboxContact(phone, {
118
+ page: Number(opts.page) || 1, pageSize: Number(opts.limit) || 25,
119
+ agentPhoneNumber: opts.number, channel: opts.channel,
120
+ }), (res) => {
121
+ const rows = res?.messages || res?.chats || res?.data || [];
122
+ if (!rows.length)
123
+ return (0, ui_1.info)(`No messages for ${phone}.`);
124
+ (0, ui_1.table)(['TIME', 'ROLE', 'MESSAGE'], rows.map((r) => [
125
+ r.timestamp ?? r.createdAt ?? '', r.role ?? r.sender ?? r.direction ?? '', cell(r.content ?? r.message ?? r.text),
126
+ ]));
127
+ });
128
+ }
129
+ async function inboxClearCommand(phone, opts) {
130
+ await guarded(`Clear all chat history for ${phone}?`, opts, () => (0, dashboardApi_1.dashboardApi)().inboxClearContact(phone, opts.number), `Cleared ${phone}.`);
131
+ }
132
+ async function inboxDeassignCommand(phone, opts) {
133
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().inboxDeassignChat({ phoneNumber: phone, agentPhoneNumber: opts.number }), ok(`Deassigned the chat with ${phone}.`));
134
+ }
135
+ // --- WhatsApp campaigns --------------------------------------------------------
136
+ async function waCampaignsListCommand(opts) {
137
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().listWaCampaigns(), list('waCampaign', 'No WhatsApp campaigns.', [
138
+ ['ID', id], ['NAME', (r) => r.waCampaignName ?? r.name ?? r.campaignName], ['TEMPLATE', (r) => r.templateName], ['SENT', (r) => `${r.sentCount ?? 0}/${r.recipientCount ?? 0}`], ['CREATED', (r) => r.createdAt],
139
+ ]));
140
+ }
141
+ async function waCampaignAnalyticsCommand(campaignId, opts) {
142
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().waCampaignAnalytics(campaignId), (res) => {
143
+ const flat = Object.entries(res?.analytics ?? res ?? {}).filter(([, v]) => typeof v !== 'object');
144
+ if (!flat.length)
145
+ return (0, ui_1.info)('No analytics yet.');
146
+ (0, ui_1.table)(['METRIC', 'VALUE'], flat);
147
+ });
148
+ }
149
+ async function waCampaignTimeseriesCommand(campaignId, opts) {
150
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().waCampaignTimeseries(campaignId, { start: opts.start, end: opts.end }), (res) => {
151
+ const rows = Array.isArray(res) ? res : res?.timeseries || res?.data || [];
152
+ if (!rows.length)
153
+ return (0, ui_1.info)('No data in that range.');
154
+ const cols = Object.keys(rows[0]);
155
+ (0, ui_1.table)(cols, rows.map((r) => cols.map((c) => cell(r[c]))));
156
+ });
157
+ }
158
+ async function waCampaignExportCommand(campaignId, opts) {
159
+ try {
160
+ const { bytes, filename } = await (0, dashboardApi_1.dashboardApi)().exportWaCampaignContacts(campaignId, {
161
+ export_fields: opts.fields, include_chat: opts.includeChat,
162
+ });
163
+ const file = opts.file || filename || `wa_campaign_${campaignId}.csv`;
164
+ (0, fs_1.writeFileSync)(file, bytes);
165
+ (0, ui_1.success)(`Wrote ${file} (${bytes.length} bytes).`);
166
+ }
167
+ catch (err) {
168
+ (0, ui_1.handleError)(err);
169
+ }
170
+ }
171
+ async function waCampaignSendCommand(opts) {
172
+ try {
173
+ const body = (0, setJson_1.readSetJson)(opts.set);
174
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().sendWaCampaign(body), ok('Campaign send requested.'));
175
+ }
176
+ catch (err) {
177
+ (0, ui_1.handleError)(err);
178
+ }
179
+ }
180
+ // --- Fine-tuning ---------------------------------------------------------------
181
+ async function finetuneListCommand(opts) {
182
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().listFinetuneRecords({ page: Number(opts.page) || 1 }), list('records', 'No fine-tune records.', [
183
+ ['CALL', (r) => r.callId ?? r.call_id ?? id(r)], ['BUCKET', (r) => r.bucket ?? r.bucketName], ['CREATED', (r) => r.createdAt],
184
+ ]));
185
+ }
186
+ async function finetuneBucketsCommand(opts) {
187
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().listFinetuneBuckets(), list('buckets', 'No buckets.', [['ID', id], ['NAME', (r) => r.name]]));
188
+ }
189
+ async function finetuneCreateBucketCommand(name, opts) {
190
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().createFinetuneBucket(name), ok(`Created bucket "${name}".`));
191
+ }
192
+ async function finetuneJobsCommand(opts) {
193
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().listFinetuneJobs({ page: Number(opts.page) || 1, pageSize: Number(opts.limit) || 100 }), list('jobs', 'No fine-tune jobs.', [
194
+ ['JOB', (r) => r.job_id ?? r.jobId ?? id(r)], ['STATUS', (r) => r.status], ['MODEL', (r) => r.model ?? r.base_model], ['CREATED', (r) => r.createdAt],
195
+ ]));
196
+ }
197
+ async function finetuneStatusCommand(jobId, opts) {
198
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().finetuneJobStatus(jobId), (res) => {
199
+ (0, ui_1.info)(`Job ${jobId}: ${res?.status ?? res?.job?.status ?? JSON.stringify(res)}`);
200
+ });
201
+ }
202
+ async function finetuneStartCommand(opts) {
203
+ try {
204
+ const body = (0, setJson_1.readSetJson)(opts.set);
205
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().startFinetuneProcess(body), ok('Fine-tune job started.'));
206
+ }
207
+ catch (err) {
208
+ (0, ui_1.handleError)(err);
209
+ }
210
+ }
211
+ async function finetuneAddModelCommand(jobId, opts) {
212
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().addFinetunedModel(jobId), ok(`Added the fine-tuned model from job ${jobId}.`));
213
+ }
214
+ async function finetuneCancelCommand(jobId, opts) {
215
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().cancelFinetuneJob(jobId), ok(`Cancelled job ${jobId}.`));
216
+ }
217
+ async function finetuneDeleteRecordCommand(callId, opts) {
218
+ await guarded(`Delete fine-tune record ${callId}?`, opts, () => (0, dashboardApi_1.dashboardApi)().deleteFinetuneRecord(callId), `Deleted record ${callId}.`);
219
+ }
220
+ // --- Team ----------------------------------------------------------------------
221
+ async function teamMembersCommand(opts) {
222
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().listTeamMembers(), list('members', 'No team members.', [
223
+ ['ID', id], ['NAME', (r) => r.name], ['EMAIL', (r) => r.email], ['PERMISSIONS', (r) => (r.permissions || []).join(', ')],
224
+ ]));
225
+ }
226
+ async function teamRolesCommand(opts) {
227
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().listTeamRoles(), list('roles', 'No roles.', [['ID', id], ['NAME', (r) => r.name], ['PERMISSIONS', (r) => (r.permissions || []).join(', ')]]));
228
+ }
229
+ async function teamCreateRoleCommand(name, opts) {
230
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().createTeamRole({ name, permissions: splitList(opts.permissions) }), ok(`Created role "${name}".`));
231
+ }
232
+ async function teamAddMemberCommand(opts) {
233
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().addTeamMember({
234
+ name: opts.name, email: opts.email,
235
+ ...(opts.password ? { password: opts.password } : {}),
236
+ ...(opts.permissions ? { permissions: splitList(opts.permissions) } : {}),
237
+ }), ok(`Added ${opts.email}.`));
238
+ }
239
+ async function teamUpdateMemberCommand(memberId, opts) {
240
+ const body = {
241
+ ...(opts.name ? { name: opts.name } : {}),
242
+ ...(opts.email ? { email: opts.email } : {}),
243
+ ...(opts.password ? { password: opts.password } : {}),
244
+ ...(opts.permissions ? { permissions: splitList(opts.permissions) } : {}),
245
+ };
246
+ if (!Object.keys(body).length)
247
+ return (0, ui_1.handleError)(new Error('Nothing to update: pass --name, --email, --password or --permissions.'));
248
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().updateTeamMember(memberId, body), ok(`Updated member ${memberId}.`));
249
+ }
250
+ async function teamRemoveMemberCommand(memberId, opts) {
251
+ await guarded(`Remove team member ${memberId}?`, opts, () => (0, dashboardApi_1.dashboardApi)().removeTeamMember(memberId), `Removed member ${memberId}.`);
252
+ }
253
+ /** `--headers 'Key:Value,Key2:Value2'` into the server's `[{key_name, value}]`. */
254
+ function parseHeaders(raw) {
255
+ return splitList(raw).map((pair) => {
256
+ const i = pair.indexOf(':');
257
+ if (i < 1)
258
+ throw new Error(`--headers entry "${pair}" must look like Key:Value.`);
259
+ return { key_name: pair.slice(0, i).trim(), value: pair.slice(i + 1).trim() };
260
+ });
261
+ }
262
+ async function teamSetWebhookCommand(opts) {
263
+ try {
264
+ if (!opts.url && !opts.analysisUrl)
265
+ throw new Error('Pass --url <webhook> and/or --analysis-url <call analysis webhook>.');
266
+ const api = (0, dashboardApi_1.dashboardApi)();
267
+ const results = {};
268
+ // The server's own field name is `callAalysisWebhook` (sic); the API method owns that spelling.
269
+ if (opts.analysisUrl)
270
+ results.callAnalysis = await api.updateCallAnalysisWebhook(opts.analysisUrl);
271
+ if (opts.url) {
272
+ results.webhook = await api.updateTeamWebhook({
273
+ webhook: opts.url,
274
+ ...(opts.headers ? { webhook_headers: parseHeaders(opts.headers) } : {}),
275
+ ...(opts.events ? { webhook_events: splitList(opts.events) } : {}),
276
+ });
277
+ }
278
+ if (opts.json)
279
+ return void console.log(JSON.stringify(results, null, 2));
280
+ (0, ui_1.success)(`Updated ${Object.keys(results).map((k) => (k === 'callAnalysis' ? 'call analysis webhook' : 'webhook')).join(' and ')}.`);
281
+ }
282
+ catch (err) {
283
+ (0, ui_1.handleError)(err);
284
+ }
285
+ }
286
+ // --- Compliance & verified numbers ---------------------------------------------
287
+ async function complianceListCommand(opts) {
288
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().listCompliance(), list('compliances', 'No compliance records.', [
289
+ ['ID', id], ['COMPANY', (r) => r.companyName], ['GST', (r) => r.gstNumber], ['USER', (r) => r.username], ['STATUS', (r) => r.status],
290
+ ]));
291
+ }
292
+ async function complianceCreateCommand(opts) {
293
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().createCompliance({
294
+ gstNumber: opts.gst, username: opts.username, companyName: opts.company,
295
+ ...(opts.gstUrl ? { gstFileUrl: opts.gstUrl } : {}),
296
+ ...(opts.cinUrl ? { cinFileUrl: opts.cinUrl } : {}),
297
+ }), ok(`Created compliance record for ${opts.company}.`));
298
+ }
299
+ async function complianceDeleteCommand(complianceId, opts) {
300
+ await guarded(`Delete compliance record ${complianceId}?`, opts, () => (0, dashboardApi_1.dashboardApi)().deleteCompliance(complianceId), `Deleted ${complianceId}.`);
301
+ }
302
+ async function verifiedNumbersListCommand(opts) {
303
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().listVerifiedNumbers(), list('numbers', 'No verified numbers.', [['ID', id], ['NUMBER', (r) => r.phoneNumber], ['VERIFIED', (r) => r.verified ?? r.isVerified]]));
304
+ }
305
+ async function verifiedNumbersAddCommand(phoneNumber, opts) {
306
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().addVerifiedNumber(phoneNumber), ok(`OTP sent to ${phoneNumber}. Finish with: cku verified-numbers verify ${phoneNumber} --otp <code>`));
307
+ }
308
+ async function verifiedNumbersVerifyCommand(phoneNumber, opts) {
309
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().verifyPhoneNumberOtp(phoneNumber, opts.otp), ok(`Verified ${phoneNumber}.`));
310
+ }
311
+ async function verifiedNumbersDeleteCommand(numberId, opts) {
312
+ await guarded(`Delete verified number ${numberId}?`, opts, () => (0, dashboardApi_1.dashboardApi)().deleteVerifiedNumber(numberId), `Deleted ${numberId}.`);
313
+ }
314
+ // --- Integrations ---------------------------------------------------------------
315
+ async function integrationsListCommand(opts) {
316
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().listIntegrations(), list('integrations', 'No integrations.', [['ID', id], ['TYPE', (r) => r.type], ['TAG', (r) => r.tag]]));
317
+ }
318
+ async function integrationsCreateCommand(opts) {
319
+ try {
320
+ const body = {
321
+ type: opts.type,
322
+ ...(opts.set ? { credentials: (0, setJson_1.readSetJson)(opts.set) } : {}),
323
+ ...(opts.tag ? { tag: opts.tag } : {}),
324
+ };
325
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().createIntegration(body), ok(`Created ${opts.type} integration.`));
326
+ }
327
+ catch (err) {
328
+ (0, ui_1.handleError)(err);
329
+ }
330
+ }
331
+ async function integrationsUpdateCommand(integrationId, opts) {
332
+ try {
333
+ const body = {
334
+ ...(opts.type ? { type: opts.type } : {}),
335
+ ...(opts.set ? { credentials: (0, setJson_1.readSetJson)(opts.set) } : {}),
336
+ ...(opts.tag ? { tag: opts.tag } : {}),
337
+ };
338
+ if (!Object.keys(body).length)
339
+ throw new Error('Nothing to update: pass --type, --set or --tag.');
340
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().updateIntegration(integrationId, body), ok(`Updated integration ${integrationId}.`));
341
+ }
342
+ catch (err) {
343
+ (0, ui_1.handleError)(err);
344
+ }
345
+ }
346
+ async function integrationsDeleteCommand(integrationId, opts) {
347
+ await guarded(`Delete integration ${integrationId}?`, opts, () => (0, dashboardApi_1.dashboardApi)().deleteIntegration(integrationId), `Deleted ${integrationId}.`);
348
+ }
349
+ async function integrationsSipCommand(opts) {
350
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().listSipTrunks(), list('sipTrunks', 'No SIP trunks.', [['ID', id], ['NAME', (r) => r.name ?? r.trunkName], ['NUMBER', (r) => r.phoneNumber ?? r.number]]));
351
+ }
352
+ async function integrationsAisensyCommand(opts) {
353
+ if (opts.agent) {
354
+ return run(opts, () => (0, dashboardApi_1.dashboardApi)().setAisensyConfig(opts.agent), ok(`Linked AiSensy to agent ${opts.agent}.`));
355
+ }
356
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().listAisensyCampaigns(), (res) => {
357
+ const rows = res?.campaigns || res?.data || (Array.isArray(res) ? res : []);
358
+ if (!rows.length)
359
+ return (0, ui_1.info)('No AiSensy campaigns.');
360
+ (0, ui_1.table)(['ID', 'NAME'], rows.map((r) => [r.id ?? r._id, r.name ?? r.campaignName]));
361
+ });
362
+ }
363
+ async function integrationsHubspotCommand(agentId, opts) {
364
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().setHubspotConfig(agentId), ok(`Linked HubSpot to agent ${agentId}.`));
365
+ }
366
+ async function integrationsShopifyCommand(opts) {
367
+ try {
368
+ const body = (0, setJson_1.readSetJson)(opts.set);
369
+ await run(opts, () => (0, dashboardApi_1.dashboardApi)().setShopifyConfig(body), ok('Shopify config saved.'));
370
+ }
371
+ catch (err) {
372
+ (0, ui_1.handleError)(err);
373
+ }
374
+ }
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.voicesCommand = voicesCommand;
4
+ exports.voicesPreviewCommand = voicesPreviewCommand;
5
+ const fs_1 = require("fs");
6
+ const dashboardApi_1 = require("../lib/dashboardApi");
7
+ const versions_1 = require("../lib/versions");
8
+ const setJson_1 = require("./setJson");
4
9
  const session_1 = require("../lib/session");
5
10
  const ui_1 = require("../lib/ui");
6
11
  const slug = (value) => String(value).toLowerCase().replace(/[^a-z0-9]/g, '');
@@ -209,3 +214,27 @@ async function voicesCommand(opts) {
209
214
  (0, ui_1.handleError)(err);
210
215
  }
211
216
  }
217
+ /** `cku voice-preview --text <t> (--agent <id> [--versions <v>] | --set <voice_configuration>)` */
218
+ async function voicesPreviewCommand(opts) {
219
+ try {
220
+ let voiceConfig;
221
+ if (opts.set) {
222
+ const parsed = (0, setJson_1.readSetJson)(opts.set);
223
+ voiceConfig = parsed.voice_configuration ?? parsed;
224
+ }
225
+ else if (opts.agent) {
226
+ const res = await (0, session_1.requireApi)().getAgent(opts.agent, opts.versions ? (0, versions_1.firstVersion)(opts.versions) : undefined);
227
+ voiceConfig = res.agent.voice_configuration;
228
+ }
229
+ else {
230
+ throw new Error('Pass --agent <agentId> [--versions <v>] or --set <voice_configuration JSON>.');
231
+ }
232
+ const { bytes, filename } = await (0, dashboardApi_1.dashboardApi)().voicePreview(voiceConfig, opts.text);
233
+ const file = opts.file || filename || 'voice_preview.mp3';
234
+ (0, fs_1.writeFileSync)(file, bytes);
235
+ (0, ui_1.success)(`Wrote ${file} (${bytes.length} bytes).`);
236
+ }
237
+ catch (err) {
238
+ (0, ui_1.handleError)(err);
239
+ }
240
+ }