callkaro-cli-unofficial 0.10.0 → 0.10.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 +6 -20
- package/dist/commands/agentCommits.js +102 -0
- package/dist/commands/agentVersions.js +3 -102
- package/dist/commands/agents.js +3 -5
- package/dist/commands/auditReports.js +275 -0
- package/dist/commands/chatAgents.js +41 -17
- package/dist/commands/chatAuditStrategies.js +0 -20
- package/dist/commands/contacts.js +32 -15
- package/dist/commands/models.js +46 -88
- package/dist/commands/platformExtras.js +0 -25
- package/dist/commands/transcribers.js +36 -65
- package/dist/commands/update.js +73 -27
- package/dist/commands/whatsappStats.js +105 -0
- package/dist/commands/workflows.js +6 -18
- package/dist/index.js +124 -26
- package/dist/lib/agentUpdate.js +4 -116
- package/dist/lib/api.js +41 -0
- package/dist/lib/config.js +1 -1
- package/dist/lib/dashboardApi.js +1 -43
- package/dist/lib/dashboardAuth.js +0 -33
- package/dist/lib/liveCatalogCheck.js +99 -0
- package/dist/lib/ui.js +15 -0
- package/dist/lib/updateCheck.js +3 -2
- package/package.json +2 -5
- package/dist/data/catalog.json +0 -1976
- package/dist/lib/catalog.js +0 -136
|
@@ -44,16 +44,14 @@ exports.chatAgentsCreateCommand = chatAgentsCreateCommand;
|
|
|
44
44
|
exports.chatAgentsUpdateCommand = chatAgentsUpdateCommand;
|
|
45
45
|
exports.chatAgentsCreateVersionCommand = chatAgentsCreateVersionCommand;
|
|
46
46
|
exports.chatAgentsPublishCommand = chatAgentsPublishCommand;
|
|
47
|
-
exports.
|
|
47
|
+
exports.chatAgentsCompareCommand = chatAgentsCompareCommand;
|
|
48
48
|
exports.chatAgentsDeleteVersionCommand = chatAgentsDeleteVersionCommand;
|
|
49
49
|
exports.chatAgentsImportVersionsCommand = chatAgentsImportVersionsCommand;
|
|
50
50
|
exports.chatAgentsImportCommand = chatAgentsImportCommand;
|
|
51
51
|
exports.chatAgentsCommitsCommand = chatAgentsCommitsCommand;
|
|
52
52
|
exports.chatAgentsCommitCommand = chatAgentsCommitCommand;
|
|
53
53
|
exports.chatAgentsRevertCommand = chatAgentsRevertCommand;
|
|
54
|
-
const dashboardApi_1 = require("../lib/dashboardApi");
|
|
55
54
|
const session_1 = require("../lib/session");
|
|
56
|
-
const dashboardAuth_1 = require("../lib/dashboardAuth");
|
|
57
55
|
const fs_1 = require("fs");
|
|
58
56
|
const ui_1 = require("../lib/ui");
|
|
59
57
|
const looseJson_1 = require("../lib/looseJson");
|
|
@@ -218,7 +216,6 @@ async function chatAgentsCreateCommand(json, opts) {
|
|
|
218
216
|
async function chatAgentsUpdateCommand(agentId, opts) {
|
|
219
217
|
const api = (0, session_1.requireApi)();
|
|
220
218
|
try {
|
|
221
|
-
await (0, dashboardAuth_1.refreshModelCatalog)();
|
|
222
219
|
const patch = await readChatAgentJson(opts.set, undefined, '--set object');
|
|
223
220
|
const res = await api.updateChatAgent(agentId, (0, versions_1.firstVersion)(opts.versions), patch);
|
|
224
221
|
if (opts.json)
|
|
@@ -261,20 +258,47 @@ async function chatAgentsPublishCommand(agentId, opts) {
|
|
|
261
258
|
}
|
|
262
259
|
}
|
|
263
260
|
/** `cku chat-agents diff <chatAgentId> --versions vA,vB | --commits cA,cB` */
|
|
264
|
-
|
|
261
|
+
const lineCount = (s) => (s ? s.split('\n').length : 0);
|
|
262
|
+
/** `cku chat-agents compare <agentId> --a <id> --b <id> [--commits] [--field name]` — versions by default (official 5.4.8). */
|
|
263
|
+
async function chatAgentsCompareCommand(agentId, opts) {
|
|
265
264
|
try {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
if (
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
265
|
+
if (!opts.a || !opts.b)
|
|
266
|
+
return (0, ui_1.handleError)(new Error('--a <id> and --b <id> are required (version ids, or commit ids with --commits)'));
|
|
267
|
+
const api = (0, session_1.requireApi)();
|
|
268
|
+
const res = opts.commits
|
|
269
|
+
? await api.compareChatAgentCommits(agentId, opts.a, opts.b)
|
|
270
|
+
: await api.compareChatAgentVersions(agentId, [opts.a, opts.b]);
|
|
271
|
+
if ((0, ui_1.output)(res, opts, 'comparison'))
|
|
272
|
+
return;
|
|
273
|
+
const label = (s) => `${s.versionName || s.id} (${s.id})${s.agentMode ? ` [${s.agentMode}]` : ''}`;
|
|
274
|
+
(0, ui_1.info)(`A: ${label(res.left)}\nB: ${label(res.right)}\n`);
|
|
275
|
+
if (res.modeMismatch)
|
|
276
|
+
(0, ui_1.info)(`Different agent types (${res.modeMismatch.left} vs ${res.modeMismatch.right}): only fields both use are compared.\n`);
|
|
277
|
+
if (opts.field) {
|
|
278
|
+
const t = res.textFieldDiffs[opts.field];
|
|
279
|
+
const c = res.changedFields[opts.field];
|
|
280
|
+
if (!t && !c)
|
|
281
|
+
return (0, ui_1.handleError)(new Error(`"${opts.field}" did not change. Changed: ${Object.keys(res.changedFields).join(', ') || '(none)'}`));
|
|
282
|
+
const a = t ? t.left : JSON.stringify(c.left, null, 2);
|
|
283
|
+
const b = t ? t.right : JSON.stringify(c.right, null, 2);
|
|
284
|
+
return void console.log(`--- A\n${a}\n\n--- B\n${b}`);
|
|
285
|
+
}
|
|
286
|
+
const rows = [];
|
|
287
|
+
for (const f of Object.keys(res.changedFields)) {
|
|
288
|
+
const t = res.textFieldDiffs[f];
|
|
289
|
+
const l = res.listDiffs?.[f];
|
|
290
|
+
rows.push([f, t ? `text: ${lineCount(t.left)} → ${lineCount(t.right)} lines` : l ? `+${l.added.length} -${l.removed.length} ~${l.changed.length}` : 'value changed']);
|
|
291
|
+
}
|
|
292
|
+
if (res.nodeDiffs)
|
|
293
|
+
rows.push(['flowGraph nodes', `+${res.nodeDiffs.added.length} -${res.nodeDiffs.removed.length} ~${res.nodeDiffs.changed.length}`]);
|
|
294
|
+
if (!rows.length)
|
|
295
|
+
console.log('No differences.');
|
|
296
|
+
else {
|
|
297
|
+
(0, ui_1.table)(['FIELD', 'CHANGE'], rows);
|
|
298
|
+
(0, ui_1.info)(`\n${rows.length} difference(s). Use --field <name> to see one side by side, or --json for everything (incl. per-node and per-function diffs).`);
|
|
299
|
+
}
|
|
300
|
+
if (res.notComparable?.length)
|
|
301
|
+
(0, ui_1.info)(`Not comparable (not used by both agent types): ${res.notComparable.join(', ')}`);
|
|
278
302
|
}
|
|
279
303
|
catch (err) {
|
|
280
304
|
(0, ui_1.handleError)(err);
|
|
@@ -7,8 +7,6 @@ exports.chatAuditCreateCommand = chatAuditCreateCommand;
|
|
|
7
7
|
exports.chatAuditUpdateCommand = chatAuditUpdateCommand;
|
|
8
8
|
exports.chatAuditDeleteCommand = chatAuditDeleteCommand;
|
|
9
9
|
exports.chatAuditBatchCommand = chatAuditBatchCommand;
|
|
10
|
-
exports.chatAuditReportsCommand = chatAuditReportsCommand;
|
|
11
|
-
const dashboardApi_1 = require("../lib/dashboardApi");
|
|
12
10
|
const session_1 = require("../lib/session");
|
|
13
11
|
const ui_1 = require("../lib/ui");
|
|
14
12
|
const setJson_1 = require("./setJson");
|
|
@@ -142,21 +140,3 @@ async function chatAuditBatchCommand(agentId, strategyId, opts) {
|
|
|
142
140
|
(0, ui_1.handleError)(err);
|
|
143
141
|
}
|
|
144
142
|
}
|
|
145
|
-
/** `cku chat-audit reports [--agent <id>] [--page <n>]` — dashboard session. */
|
|
146
|
-
async function chatAuditReportsCommand(opts) {
|
|
147
|
-
try {
|
|
148
|
-
const res = await (0, dashboardApi_1.dashboardApi)().chatAuditReportBatches({
|
|
149
|
-
agentId: opts.agent,
|
|
150
|
-
page: opts.page ? Number(opts.page) : undefined,
|
|
151
|
-
});
|
|
152
|
-
if (opts.json)
|
|
153
|
-
return void console.log(JSON.stringify(res, null, 2));
|
|
154
|
-
const rows = res?.data?.batches || res?.batches || (Array.isArray(res?.data) ? res.data : []);
|
|
155
|
-
if (!rows.length)
|
|
156
|
-
return void (0, ui_1.info)('No chat audit report batches.');
|
|
157
|
-
(0, ui_1.table)(['ID', 'STATUS', 'CREATED'], rows.map((r) => [r._id || r.id || '', r.status || '', r.createdAt || '']));
|
|
158
|
-
}
|
|
159
|
-
catch (err) {
|
|
160
|
-
(0, ui_1.handleError)(err);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.collect = void 0;
|
|
4
4
|
exports.contactsListCommand = contactsListCommand;
|
|
5
|
+
exports.contactsMessagesCommand = contactsMessagesCommand;
|
|
5
6
|
exports.contactsGetCommand = contactsGetCommand;
|
|
6
7
|
exports.contactsCreateCommand = contactsCreateCommand;
|
|
7
8
|
exports.contactsUpdateCommand = contactsUpdateCommand;
|
|
@@ -53,18 +54,6 @@ const attrValue = (c, name) => {
|
|
|
53
54
|
const v = a && typeof a === 'object' && 'value' in a ? a.value : a;
|
|
54
55
|
return v === undefined || v === null ? '' : typeof v === 'object' ? JSON.stringify(v) : String(v);
|
|
55
56
|
};
|
|
56
|
-
function output(value, opts, label) {
|
|
57
|
-
if (opts.file) {
|
|
58
|
-
(0, fs_1.writeFileSync)(opts.file, JSON.stringify(value, null, 2));
|
|
59
|
-
(0, ui_1.success)(`Saved ${label} to ${opts.file}`);
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
if (opts.json) {
|
|
63
|
-
console.log(JSON.stringify(value, null, 2));
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
57
|
// ---- contacts -------------------------------------------------------------
|
|
69
58
|
async function contactsListCommand(opts) {
|
|
70
59
|
const api = (0, session_1.requireApi)();
|
|
@@ -75,7 +64,7 @@ async function contactsListCommand(opts) {
|
|
|
75
64
|
page: opts.page,
|
|
76
65
|
pageSize: opts.pageSize,
|
|
77
66
|
});
|
|
78
|
-
if (output({ contacts, pagination }, opts, 'contacts'))
|
|
67
|
+
if ((0, ui_1.output)({ contacts, pagination }, opts, 'contacts'))
|
|
79
68
|
return;
|
|
80
69
|
if (!contacts.length)
|
|
81
70
|
return void (0, ui_1.info)('No contacts match.');
|
|
@@ -89,11 +78,39 @@ async function contactsListCommand(opts) {
|
|
|
89
78
|
(0, ui_1.handleError)(err);
|
|
90
79
|
}
|
|
91
80
|
}
|
|
81
|
+
/** `cku contacts messages <phone>` — a contact's chat messages, newest first (official 5.4.8). */
|
|
82
|
+
async function contactsMessagesCommand(phone, opts) {
|
|
83
|
+
try {
|
|
84
|
+
const result = await (0, session_1.requireApi)().getContactMessages(phone.replace(/\D/g, ''), {
|
|
85
|
+
agentPhoneNumber: opts.agentPhoneNumber?.replace(/\D/g, ''),
|
|
86
|
+
channel: opts.channel,
|
|
87
|
+
threadId: opts.threadId,
|
|
88
|
+
page: opts.page,
|
|
89
|
+
pageSize: opts.pageSize,
|
|
90
|
+
});
|
|
91
|
+
if ((0, ui_1.output)(result, opts, 'contact messages'))
|
|
92
|
+
return;
|
|
93
|
+
if (!result.messages?.length)
|
|
94
|
+
return void (0, ui_1.info)('No messages found for this contact.');
|
|
95
|
+
(0, ui_1.table)(['TIME', 'DIRECTION', 'CHANNEL', 'MESSAGE', 'STATUS'], result.messages.map((m) => [
|
|
96
|
+
String(m.createdAt || '').slice(0, 19), // drop milliseconds
|
|
97
|
+
m.direction || '',
|
|
98
|
+
m.channel || '',
|
|
99
|
+
String(m.body || m.contentLink || m.template || '').replace(/\s+/g, ' ').slice(0, 100),
|
|
100
|
+
m.status || '',
|
|
101
|
+
]));
|
|
102
|
+
const { page, totalPages, total, hasNext } = result.pagination || {};
|
|
103
|
+
(0, ui_1.info)(`Page ${page ?? 1}/${totalPages || 1} - ${total ?? result.messages.length} message(s)${hasNext ? ' (use --page to see more)' : ''}`);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
(0, ui_1.handleError)(err);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
92
109
|
async function contactsGetCommand(phone, opts) {
|
|
93
110
|
const api = (0, session_1.requireApi)();
|
|
94
111
|
try {
|
|
95
112
|
const { contact } = await api.getContact(phone);
|
|
96
|
-
if (output(contact, opts, 'contact'))
|
|
113
|
+
if ((0, ui_1.output)(contact, opts, 'contact'))
|
|
97
114
|
return;
|
|
98
115
|
(0, ui_1.keyValue)({
|
|
99
116
|
Phone: contact.phone_number,
|
|
@@ -303,7 +320,7 @@ async function attributesListCommand(opts) {
|
|
|
303
320
|
const api = (0, session_1.requireApi)();
|
|
304
321
|
try {
|
|
305
322
|
const { definitions } = await api.listContactAttributes();
|
|
306
|
-
if (output(definitions, opts, 'attributes'))
|
|
323
|
+
if ((0, ui_1.output)(definitions, opts, 'attributes'))
|
|
307
324
|
return;
|
|
308
325
|
if (!definitions.length) {
|
|
309
326
|
return void (0, ui_1.info)('No attributes defined yet. Create one with `cku contacts attributes set <name>`.');
|
package/dist/commands/models.js
CHANGED
|
@@ -1,100 +1,58 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.slotsFromLlms = slotsFromLlms;
|
|
3
4
|
exports.modelsCommand = modelsCommand;
|
|
4
|
-
const
|
|
5
|
+
const session_1 = require("../lib/session");
|
|
5
6
|
const ui_1 = require("../lib/ui");
|
|
6
|
-
const
|
|
7
|
-
const catalog_1 = require("../lib/catalog");
|
|
7
|
+
const liveCatalogCheck_1 = require("../lib/liveCatalogCheck");
|
|
8
8
|
/**
|
|
9
|
-
* `cku models` — what the server
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* hardcoded list of 7 ids while the server accepted 44 for agent models, so
|
|
13
|
-
* `agents update --set '{"model":"…"}'` rejected values that work fine in the
|
|
14
|
-
* dashboard.
|
|
15
|
-
*
|
|
16
|
-
* With a dashboard session this reads the live `/auth/get-user` payload, which is
|
|
17
|
-
* the same one the validator uses, so the two cannot drift apart. Without one it
|
|
18
|
-
* reads the identical lists out of `catalog.json` — the file `catalog:sync` last
|
|
19
|
-
* overlaid from the server, and the one `npm run catalog:check` compares against
|
|
20
|
-
* it. Either way the answer is the server's; the only difference is how fresh.
|
|
9
|
+
* `cku models` — what the server accepts, per slot, from `GET /cli/llms` (the account's own list;
|
|
10
|
+
* Spinny and an internal account differ). The same list `agents update` validates against
|
|
11
|
+
* (lib/liveCatalogCheck.ts), so a value copied from here is not rejected a moment later.
|
|
21
12
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
]
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'call-analyzer', 'ai-auditor', 'kpi-rca', 'workflow-analytics',
|
|
31
|
-
];
|
|
32
|
-
/**
|
|
33
|
-
* The server's lists, or the shipped copy. A missing dashboard session is an
|
|
34
|
-
* ordinary state, not an error — the shipped table is good enough to plan an
|
|
35
|
-
* update with, and refusing to print it would push people back to guessing.
|
|
36
|
-
*/
|
|
37
|
-
async function readSlots() {
|
|
38
|
-
try {
|
|
39
|
-
const res = await (0, dashboardApi_1.dashboardApi)().getUser();
|
|
40
|
-
const llm = res?.catalogs?.llmModels || {};
|
|
41
|
-
if (!Object.keys(llm).length)
|
|
42
|
-
throw new Error('the response carried no model catalogue');
|
|
43
|
-
// Keep the validator in step with what is on screen, so a value copied from
|
|
44
|
-
// this output is not rejected a moment later.
|
|
45
|
-
(0, agentUpdate_1.applyLiveModels)(res?.catalogs);
|
|
46
|
-
// Lists are per account, so name whose they are.
|
|
47
|
-
return { llm, source: `the server, now${res?.email ? ` (${res.email})` : ''}` };
|
|
48
|
-
}
|
|
49
|
-
catch (err) {
|
|
50
|
-
if (!Object.keys(catalog_1.LLM_MODELS).length) {
|
|
51
|
-
return { llm: {}, source: '', error: err };
|
|
52
|
-
}
|
|
53
|
-
return { llm: catalog_1.LLM_MODELS, source: 'catalog.json, last synced from the server' };
|
|
54
|
-
}
|
|
13
|
+
const NOTES = { model: "the agent's own LLM", secondary_model: 'secondary LLM', postcallmodel: 'post-call analysis' };
|
|
14
|
+
/** `{slot: [model ids]}` from the catalogue's per-model category tags. */
|
|
15
|
+
function slotsFromLlms(models) {
|
|
16
|
+
const slots = {};
|
|
17
|
+
for (const m of models)
|
|
18
|
+
for (const c of m.categories || [])
|
|
19
|
+
(slots[c.id] ||= []).push(m.id);
|
|
20
|
+
return slots;
|
|
55
21
|
}
|
|
56
22
|
async function modelsCommand(opts) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return (0, ui_1.handleError)(new Error(`No "${opts.slot}" slot in the server catalogue.\n Slots: ${known}`));
|
|
23
|
+
try {
|
|
24
|
+
const llm = slotsFromLlms((await (0, session_1.requireApi)().getLlms()).models || []);
|
|
25
|
+
if (opts.json)
|
|
26
|
+
return void console.log(JSON.stringify(llm, null, 2));
|
|
27
|
+
if (opts.slot) {
|
|
28
|
+
const list = llm[opts.slot];
|
|
29
|
+
if (!list)
|
|
30
|
+
return (0, ui_1.handleError)(new Error(`No "${opts.slot}" slot in the server catalogue.\n Slots: ${Object.keys(llm).sort().join(', ')}`));
|
|
31
|
+
(0, ui_1.info)(`${opts.slot} · ${list.length} model(s)\n`);
|
|
32
|
+
for (const m of list)
|
|
33
|
+
(0, ui_1.info)(` ${m}`);
|
|
34
|
+
return;
|
|
70
35
|
}
|
|
71
|
-
(0, ui_1.info)(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (agentList.length > 12)
|
|
90
|
-
(0, ui_1.info)(` … and ${agentList.length - 12} more — cku models --slot agent`);
|
|
91
|
-
}
|
|
92
|
-
const post = llm['post-call'] || [];
|
|
93
|
-
if (post.length) {
|
|
94
|
-
(0, ui_1.info)(`\n${post.length} for --set postcallmodel: ${post.join(', ')}`);
|
|
36
|
+
(0, ui_1.info)('Model slots, and the fields each one governs:\n');
|
|
37
|
+
(0, ui_1.table)(['FIELD', 'SLOT', 'COUNT', 'GOVERNS'], Object.entries(liveCatalogCheck_1.LLM_FIELDS).map(([f, s]) => [f, s, (llm[s] || []).length, NOTES[f]]));
|
|
38
|
+
const fieldSlots = new Set(Object.values(liveCatalogCheck_1.LLM_FIELDS));
|
|
39
|
+
const other = Object.keys(llm).filter((s) => !fieldSlots.has(s));
|
|
40
|
+
if (other.length) {
|
|
41
|
+
(0, ui_1.info)('\nOther slots the server exposes (not agent fields):');
|
|
42
|
+
(0, ui_1.table)(['SLOT', 'COUNT'], other.map((s) => [s, llm[s].length]));
|
|
43
|
+
}
|
|
44
|
+
const agentList = llm.agent || [];
|
|
45
|
+
if (agentList.length) {
|
|
46
|
+
(0, ui_1.info)(`\n${agentList.length} models for --set model / secondary_model, including:`);
|
|
47
|
+
(0, ui_1.info)(` ${agentList.slice(0, 12).join(', ')}`);
|
|
48
|
+
if (agentList.length > 12)
|
|
49
|
+
(0, ui_1.info)(` … and ${agentList.length - 12} more — cku models --slot agent`);
|
|
50
|
+
}
|
|
51
|
+
const post = llm['post-call'] || [];
|
|
52
|
+
if (post.length)
|
|
53
|
+
(0, ui_1.info)(`\n${post.length} for --set postcallmodel: ${post.join(', ')}`);
|
|
95
54
|
}
|
|
96
|
-
(
|
|
97
|
-
|
|
98
|
-
(0, ui_1.info)(' `cku dashboard login` re-reads it live; `npm run catalog:check` reports how far it has drifted.');
|
|
55
|
+
catch (err) {
|
|
56
|
+
(0, ui_1.handleError)(err);
|
|
99
57
|
}
|
|
100
58
|
}
|
|
@@ -7,9 +7,6 @@ exports.inboxReleaseHandoffCommand = inboxReleaseHandoffCommand;
|
|
|
7
7
|
exports.inboxContactCommand = inboxContactCommand;
|
|
8
8
|
exports.inboxClearCommand = inboxClearCommand;
|
|
9
9
|
exports.inboxDeassignCommand = inboxDeassignCommand;
|
|
10
|
-
exports.waCampaignsListCommand = waCampaignsListCommand;
|
|
11
|
-
exports.waCampaignAnalyticsCommand = waCampaignAnalyticsCommand;
|
|
12
|
-
exports.waCampaignTimeseriesCommand = waCampaignTimeseriesCommand;
|
|
13
10
|
exports.waCampaignExportCommand = waCampaignExportCommand;
|
|
14
11
|
exports.waCampaignSendCommand = waCampaignSendCommand;
|
|
15
12
|
exports.finetuneListCommand = finetuneListCommand;
|
|
@@ -133,28 +130,6 @@ async function inboxDeassignCommand(phone, opts) {
|
|
|
133
130
|
await run(opts, () => (0, dashboardApi_1.dashboardApi)().inboxDeassignChat({ phoneNumber: phone, agentPhoneNumber: opts.number }), ok(`Deassigned the chat with ${phone}.`));
|
|
134
131
|
}
|
|
135
132
|
// --- 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
133
|
async function waCampaignExportCommand(campaignId, opts) {
|
|
159
134
|
try {
|
|
160
135
|
const { bytes, filename } = await (0, dashboardApi_1.dashboardApi)().exportWaCampaignContacts(campaignId, {
|
|
@@ -1,93 +1,64 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.transcribersCommand = transcribersCommand;
|
|
4
|
+
const session_1 = require("../lib/session");
|
|
4
5
|
const ui_1 = require("../lib/ui");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return p.models.map((m) => ({
|
|
17
|
-
provider: p.name,
|
|
18
|
-
model: m.name,
|
|
19
|
-
languageKind: p.languageKind || '—',
|
|
20
|
-
languages: m.languages.join(' '),
|
|
21
|
-
}));
|
|
22
|
-
}
|
|
6
|
+
/**
|
|
7
|
+
* `cku transcribers` — from `GET /cli/transcribers`, the same catalogue the dashboard and MCP use and
|
|
8
|
+
* the one `agents update` validates against (lib/liveCatalogCheck.ts). Official 5.4.8.
|
|
9
|
+
*/
|
|
10
|
+
const slug = (s) => s.toLowerCase().replace(/[\s_-]+/g, '');
|
|
11
|
+
/** "hi" matches "hi" and "hi-IN"; comparisons are case-insensitive. */
|
|
12
|
+
const langMatches = (candidate, wanted) => {
|
|
13
|
+
const a = candidate.toLowerCase();
|
|
14
|
+
const b = wanted.toLowerCase();
|
|
15
|
+
return a === b || a.split('-')[0] === b.split('-')[0];
|
|
16
|
+
};
|
|
23
17
|
/** `--provider X --fields` — the exact keys a provider's `transcriber` object takes. */
|
|
24
|
-
function showFields(
|
|
18
|
+
function showFields(provider, rows, json) {
|
|
19
|
+
const models = rows.map((r) => r.model).filter(Boolean);
|
|
20
|
+
const languages = [...new Set(rows.flatMap((r) => r.languages))];
|
|
25
21
|
if (json) {
|
|
26
|
-
return void console.log(JSON.stringify({ provider
|
|
22
|
+
return void console.log(JSON.stringify({ provider, fields: rows[0].fields, models, languageKind: rows[0].languageKind, languages, rows }, null, 2));
|
|
27
23
|
}
|
|
28
24
|
(0, ui_1.keyValue)({
|
|
29
|
-
provider
|
|
30
|
-
'transcriber keys':
|
|
31
|
-
models:
|
|
32
|
-
transcriber_language:
|
|
33
|
-
languages:
|
|
34
|
-
access: p.gate === 'admin' ? 'master admin only' : 'all accounts',
|
|
25
|
+
provider,
|
|
26
|
+
'transcriber keys': rows[0].fields.join(', '),
|
|
27
|
+
models: models.length ? models.join(', ') : '(no transcriber_model key)',
|
|
28
|
+
transcriber_language: rows[0].languageKind === 'array' ? 'array of locales' : 'single string',
|
|
29
|
+
languages: languages.join(' '),
|
|
35
30
|
});
|
|
36
|
-
if (p.notes)
|
|
37
|
-
(0, ui_1.info)(`\n${p.notes}`);
|
|
38
31
|
(0, ui_1.info)("\nCopy transcriber_language verbatim — never reformat [\"en-IN\"] to \"en\", and never wrap a string provider's value in an array.");
|
|
39
32
|
}
|
|
40
|
-
function unknownProvider(name) {
|
|
41
|
-
(0, ui_1.info)(`Unknown transcriber provider "${name}". Known: ${catalog_1.TRANSCRIBER_PROVIDERS.map((p) => p.name).join(', ')}.`);
|
|
42
|
-
process.exitCode = 1;
|
|
43
|
-
}
|
|
44
33
|
async function transcribersCommand(opts) {
|
|
45
34
|
try {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
35
|
+
const all = (await (0, session_1.requireApi)().getTranscribers()).transcribers || [];
|
|
36
|
+
let rows = all;
|
|
37
|
+
if (opts.provider) {
|
|
38
|
+
rows = all.filter((r) => slug(r.provider) === slug(opts.provider));
|
|
39
|
+
if (!rows.length) {
|
|
40
|
+
(0, ui_1.info)(`Unknown transcriber provider "${opts.provider}". Known: ${[...new Set(all.map((r) => r.provider))].join(', ')}.`);
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
51
45
|
if (opts.fields) {
|
|
52
46
|
if (!opts.provider) {
|
|
53
47
|
(0, ui_1.info)('--fields needs a provider, e.g. `cku transcribers --provider sarvam --fields`.');
|
|
54
48
|
process.exitCode = 1;
|
|
55
49
|
return;
|
|
56
50
|
}
|
|
57
|
-
|
|
58
|
-
if (!p)
|
|
59
|
-
return unknownProvider(opts.provider);
|
|
60
|
-
return showFields(p, opts.json);
|
|
61
|
-
}
|
|
62
|
-
let scoped = providers;
|
|
63
|
-
if (opts.provider) {
|
|
64
|
-
const p = (0, catalog_1.findTranscriberProvider)(opts.provider);
|
|
65
|
-
if (!p)
|
|
66
|
-
return unknownProvider(opts.provider);
|
|
67
|
-
scoped = providers.filter((x) => x.name === p.name);
|
|
68
|
-
// A gated provider asked for by name should still show, not be silently empty.
|
|
69
|
-
if (!scoped.length)
|
|
70
|
-
scoped = [p];
|
|
71
|
-
}
|
|
72
|
-
let rows = scoped.flatMap(rowsFor);
|
|
73
|
-
if (opts.language) {
|
|
74
|
-
rows = rows.filter((r) => r.languages.split(' ').some((l) => (0, catalog_1.langMatches)(l, [opts.language])));
|
|
51
|
+
return showFields(rows[0].provider, rows, opts.json);
|
|
75
52
|
}
|
|
53
|
+
if (opts.language)
|
|
54
|
+
rows = rows.filter((r) => r.languages.some((l) => langMatches(l, opts.language)));
|
|
76
55
|
if (opts.json)
|
|
77
56
|
return void console.log(JSON.stringify(rows, null, 2));
|
|
78
57
|
if (!rows.length)
|
|
79
|
-
return void
|
|
80
|
-
(0, ui_1.table)(['PROVIDER', 'MODEL', 'LANGUAGE FIELD', 'LANGUAGES'], rows.map((r) => [r.provider, r.model, r.languageKind, r.languages]));
|
|
58
|
+
return void console.log('No transcribers matched. Try fewer filters.');
|
|
59
|
+
(0, ui_1.table)(['PROVIDER', 'MODEL', 'LANGUAGE FIELD', 'LANGUAGES'], rows.map((r) => [r.provider, r.model ?? '(none)', r.languageKind, r.languages.join(' ')]));
|
|
81
60
|
(0, ui_1.info)(`\n${rows.length} provider/model combination(s).`);
|
|
82
61
|
(0, ui_1.info)('Use `cku transcribers --provider <name> --fields` for the exact keys a provider takes.');
|
|
83
|
-
if (!opts.all && catalog_1.TRANSCRIBER_PROVIDERS.some((p) => p.gate === 'admin')) {
|
|
84
|
-
(0, ui_1.info)('Admin-gated providers are hidden — add --all to include them.');
|
|
85
|
-
}
|
|
86
|
-
if (stale.length) {
|
|
87
|
-
(0, ui_1.info)('');
|
|
88
|
-
(0, ui_1.info)(`Not currently served by the backend, kept from the built-in list: ${stale.map((p) => p.name).join(', ')}`);
|
|
89
|
-
(0, ui_1.info)(' Their models are stale. Re-run `npm run catalog:sync` to drop them.');
|
|
90
|
-
}
|
|
91
62
|
}
|
|
92
63
|
catch (err) {
|
|
93
64
|
(0, ui_1.handleError)(err);
|