callkaro-cli-unofficial 0.7.2 → 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.
@@ -43,6 +43,8 @@ exports.chatAgentsCreateCommand = chatAgentsCreateCommand;
43
43
  exports.chatAgentsUpdateCommand = chatAgentsUpdateCommand;
44
44
  exports.chatAgentsCreateVersionCommand = chatAgentsCreateVersionCommand;
45
45
  exports.chatAgentsPublishCommand = chatAgentsPublishCommand;
46
+ exports.chatAgentsDiffCommand = chatAgentsDiffCommand;
47
+ const dashboardApi_1 = require("../lib/dashboardApi");
46
48
  const session_1 = require("../lib/session");
47
49
  const dashboardAuth_1 = require("../lib/dashboardAuth");
48
50
  const ui_1 = require("../lib/ui");
@@ -249,3 +251,23 @@ async function chatAgentsPublishCommand(agentId, opts) {
249
251
  (0, ui_1.handleError)(err);
250
252
  }
251
253
  }
254
+ /** `cku chat-agents diff <chatAgentId> --versions vA,vB | --commits cA,cB` */
255
+ async function chatAgentsDiffCommand(chatAgentId, opts) {
256
+ try {
257
+ const pair = (s, flag) => {
258
+ const p = (s || '').split(',').map((x) => x.trim()).filter(Boolean);
259
+ if (p.length !== 2)
260
+ throw new Error(`${flag} needs exactly two ids, comma-separated.`);
261
+ return [p[0], p[1]];
262
+ };
263
+ if (!!opts.versions === !!opts.commits)
264
+ throw new Error('Pass exactly one of --versions <vA,vB> or --commits <cA,cB>.');
265
+ const res = opts.versions
266
+ ? await (0, dashboardApi_1.dashboardApi)().compareChatAgentVersions(pair(opts.versions, '--versions'))
267
+ : await (0, dashboardApi_1.dashboardApi)().compareChatAgentCommits(chatAgentId, ...pair(opts.commits, '--commits'));
268
+ console.log(JSON.stringify(res, null, 2));
269
+ }
270
+ catch (err) {
271
+ (0, ui_1.handleError)(err);
272
+ }
273
+ }
@@ -7,6 +7,8 @@ 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");
10
12
  const session_1 = require("../lib/session");
11
13
  const ui_1 = require("../lib/ui");
12
14
  const setJson_1 = require("./setJson");
@@ -137,3 +139,21 @@ async function chatAuditBatchCommand(agentId, strategyId, opts) {
137
139
  (0, ui_1.handleError)(err);
138
140
  }
139
141
  }
142
+ /** `cku chat-audit reports [--agent <id>] [--page <n>]` — dashboard session. */
143
+ async function chatAuditReportsCommand(opts) {
144
+ try {
145
+ const res = await (0, dashboardApi_1.dashboardApi)().chatAuditReportBatches({
146
+ agentId: opts.agent,
147
+ page: opts.page ? Number(opts.page) : undefined,
148
+ });
149
+ if (opts.json)
150
+ return void console.log(JSON.stringify(res, null, 2));
151
+ const rows = res?.data?.batches || res?.batches || (Array.isArray(res?.data) ? res.data : []);
152
+ if (!rows.length)
153
+ return void (0, ui_1.info)('No chat audit report batches.');
154
+ (0, ui_1.table)(['ID', 'STATUS', 'CREATED'], rows.map((r) => [r._id || r.id || '', r.status || '', r.createdAt || '']));
155
+ }
156
+ catch (err) {
157
+ (0, ui_1.handleError)(err);
158
+ }
159
+ }
@@ -7,7 +7,13 @@ const jsonInput_1 = require("./jsonInput");
7
7
  const looseJson_1 = require("../lib/looseJson");
8
8
  const METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
9
9
  /** The route segments the path may contain — see `repairPosixPath`. */
10
- const PATH_SEGMENTS = ['auth', 'v1', 'v2', 'v3', 'kpi-manager', 'pending-tasks', 'phone-number'];
10
+ const PATH_SEGMENTS = [
11
+ 'auth', 'v1', 'v2', 'v3', 'kpi-manager', 'pending-tasks', 'phone-number',
12
+ 'analytics', 'dnd-phone-numbers', 'feedback', 'knowledge', 'openai-tools',
13
+ 'schedulecalls', 'agent-folders', 'inbox', 'finetune', 'batchcalls',
14
+ 'team-management', 'verified-phone-numbers', 'compliance', 'integrations',
15
+ 'wa-campaign', 'whatsapp', 'workflows', 'contacts', 'calls', 'utils',
16
+ ];
11
17
  /**
12
18
  * `cku dashboard api <METHOD> <path> [--body <json|@file>] [--query k=v ...]`
13
19
  *
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dndListCommand = dndListCommand;
4
+ exports.dndAddCommand = dndAddCommand;
5
+ exports.dndUpdateCommand = dndUpdateCommand;
6
+ exports.dndRemoveCommand = dndRemoveCommand;
7
+ exports.dndImportCommand = dndImportCommand;
8
+ const fs_1 = require("fs");
9
+ const dashboardApi_1 = require("../lib/dashboardApi");
10
+ const ui_1 = require("../lib/ui");
11
+ const day = (s) => {
12
+ if (!s)
13
+ return undefined;
14
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(s))
15
+ throw new Error(`Date "${s}" must be YYYY-MM-DD.`);
16
+ return s;
17
+ };
18
+ async function dndListCommand(opts) {
19
+ try {
20
+ const res = await (0, dashboardApi_1.dashboardApi)().listDndNumbers({
21
+ page: Number(opts.page) || 1,
22
+ limit: Number(opts.limit) || 20,
23
+ search: opts.search,
24
+ startDate: day(opts.since),
25
+ endDate: day(opts.until),
26
+ });
27
+ if (opts.json)
28
+ return void console.log(JSON.stringify(res, null, 2));
29
+ const rows = res?.data || res?.numbers || res?.phoneNumbers || (Array.isArray(res) ? res : []);
30
+ if (!rows.length)
31
+ return void (0, ui_1.info)('No DND numbers.');
32
+ (0, ui_1.table)(['ID', 'PHONE', 'CREATED'], rows.map((r) => [r._id || r.id || '', r.phoneNumber || '', r.createdAt || '']));
33
+ if (res?.total !== undefined)
34
+ (0, ui_1.info)(`\nTotal: ${res.total}`);
35
+ }
36
+ catch (err) {
37
+ (0, ui_1.handleError)(err);
38
+ }
39
+ }
40
+ async function dndAddCommand(phoneNumber, opts) {
41
+ try {
42
+ const res = await (0, dashboardApi_1.dashboardApi)().addDndNumber(phoneNumber);
43
+ if (opts.json)
44
+ return void console.log(JSON.stringify(res, null, 2));
45
+ (0, ui_1.success)(`Added ${phoneNumber} to DND.`);
46
+ }
47
+ catch (err) {
48
+ (0, ui_1.handleError)(err);
49
+ }
50
+ }
51
+ async function dndUpdateCommand(id, phoneNumber, opts) {
52
+ try {
53
+ const res = await (0, dashboardApi_1.dashboardApi)().updateDndNumber(id, phoneNumber);
54
+ if (opts.json)
55
+ return void console.log(JSON.stringify(res, null, 2));
56
+ (0, ui_1.success)(`Updated ${id} to ${phoneNumber}.`);
57
+ }
58
+ catch (err) {
59
+ (0, ui_1.handleError)(err);
60
+ }
61
+ }
62
+ async function dndRemoveCommand(idsCsv, opts) {
63
+ try {
64
+ const ids = idsCsv.split(',').map((s) => s.trim()).filter(Boolean);
65
+ if (!ids.length)
66
+ throw new Error('Give one or more DND record ids (comma-separated). List them with: cku dnd list');
67
+ if (!(await (0, ui_1.confirm)(`Remove ${ids.length} DND record(s)? Those numbers can be called again.`, opts.yes)))
68
+ return;
69
+ const api = (0, dashboardApi_1.dashboardApi)();
70
+ const res = ids.length === 1 ? await api.removeDndNumber(ids[0]) : await api.removeDndNumbers(ids);
71
+ if (opts.json)
72
+ return void console.log(JSON.stringify(res, null, 2));
73
+ (0, ui_1.success)(`Removed ${ids.length} DND record(s).`);
74
+ }
75
+ catch (err) {
76
+ (0, ui_1.handleError)(err);
77
+ }
78
+ }
79
+ async function dndImportCommand(opts) {
80
+ try {
81
+ if (!opts.phones && !opts.file)
82
+ throw new Error('Pass --phones <a,b,...> or --file <path> (comma/newline list or JSON array).');
83
+ const text = (opts.phones ?? (0, fs_1.readFileSync)(opts.file, 'utf8')).trim();
84
+ let phones;
85
+ if (text.startsWith('[')) {
86
+ const arr = JSON.parse(text);
87
+ if (!Array.isArray(arr))
88
+ throw new Error('JSON input must be an array of phone numbers.');
89
+ phones = arr.map((p) => String(p).trim());
90
+ }
91
+ else {
92
+ phones = text.split(/[,\n\r]+/).map((s) => s.trim());
93
+ }
94
+ phones = phones.filter(Boolean);
95
+ if (!phones.length)
96
+ throw new Error('No phone numbers found in input.');
97
+ const res = await (0, dashboardApi_1.dashboardApi)().importDndNumbers(phones);
98
+ if (opts.json)
99
+ return void console.log(JSON.stringify(res, null, 2));
100
+ (0, ui_1.success)(`Imported ${phones.length} number(s) to DND.`);
101
+ }
102
+ catch (err) {
103
+ (0, ui_1.handleError)(err);
104
+ }
105
+ }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.feedbackListCommand = feedbackListCommand;
4
+ exports.feedbackCreateCommand = feedbackCreateCommand;
5
+ exports.feedbackCommentCommand = feedbackCommentCommand;
6
+ exports.feedbackSubmitReviewCommand = feedbackSubmitReviewCommand;
7
+ exports.feedbackReopenCommand = feedbackReopenCommand;
8
+ exports.feedbackResolveCommand = feedbackResolveCommand;
9
+ const dashboardApi_1 = require("../lib/dashboardApi");
10
+ const ui_1 = require("../lib/ui");
11
+ const STATUSES = ['processing', 'in-review', 'resolved'];
12
+ const list = (s) => (s || '').split(',').map((x) => x.trim()).filter(Boolean);
13
+ const out = (json, res, msg) => json ? console.log(JSON.stringify(res, null, 2)) : (0, ui_1.success)(msg);
14
+ async function feedbackListCommand(opts) {
15
+ try {
16
+ const status = opts.status || 'processing';
17
+ if (!STATUSES.includes(status))
18
+ throw new Error(`--status must be one of: ${STATUSES.join(', ')} (got "${status}").`);
19
+ const res = await (0, dashboardApi_1.dashboardApi)().listFeedback({ status, page: Number(opts.page) || 1 });
20
+ if (opts.json)
21
+ return void console.log(JSON.stringify(res, null, 2));
22
+ const rows = res?.data || res?.feedbacks || res?.feedback || (Array.isArray(res) ? res : []);
23
+ if (!rows.length)
24
+ return void (0, ui_1.info)(`No ${status} feedback tickets.`);
25
+ (0, ui_1.table)(['ID', 'NAME', 'STATUS', 'CALL', 'ISSUE'], rows.map((r) => [r._id || r.id || '', r.feedbackName || '', r.status || status, r.issueCallId || '', String(r.callIssue || '').slice(0, 60)]));
26
+ }
27
+ catch (err) {
28
+ (0, ui_1.handleError)(err);
29
+ }
30
+ }
31
+ async function feedbackCreateCommand(opts) {
32
+ try {
33
+ const res = await (0, dashboardApi_1.dashboardApi)().createFeedback({
34
+ engineerId: opts.engineer,
35
+ callIssue: opts.issue,
36
+ feedbackName: opts.name,
37
+ issueCallId: opts.call,
38
+ ...(opts.version ? { versionId: opts.version } : {}),
39
+ });
40
+ out(opts.json, res, 'Feedback ticket created.');
41
+ }
42
+ catch (err) {
43
+ (0, ui_1.handleError)(err);
44
+ }
45
+ }
46
+ async function feedbackCommentCommand(id, text, opts) {
47
+ try {
48
+ if (!text?.trim())
49
+ throw new Error('Comment text is required.');
50
+ out(opts.json, await (0, dashboardApi_1.dashboardApi)().commentFeedback(id, text), `Comment added to ${id}.`);
51
+ }
52
+ catch (err) {
53
+ (0, ui_1.handleError)(err);
54
+ }
55
+ }
56
+ async function feedbackSubmitReviewCommand(id, opts) {
57
+ try {
58
+ const body = {
59
+ ...(opts.calls ? { resolvedCallUrls: list(opts.calls) } : {}),
60
+ ...(opts.versionUrl ? { resolvedVersionUrl: opts.versionUrl } : {}),
61
+ ...(opts.comment ? { comment: opts.comment } : {}),
62
+ };
63
+ out(opts.json, await (0, dashboardApi_1.dashboardApi)().submitFeedbackReview(id, body), `${id} submitted for review.`);
64
+ }
65
+ catch (err) {
66
+ (0, ui_1.handleError)(err);
67
+ }
68
+ }
69
+ async function feedbackReopenCommand(id, opts) {
70
+ try {
71
+ out(opts.json, await (0, dashboardApi_1.dashboardApi)().reopenFeedback(id, opts.comment), `${id} sent back to processing.`);
72
+ }
73
+ catch (err) {
74
+ (0, ui_1.handleError)(err);
75
+ }
76
+ }
77
+ async function feedbackResolveCommand(id, opts) {
78
+ try {
79
+ out(opts.json, await (0, dashboardApi_1.dashboardApi)().resolveFeedback(id, opts.comment), `${id} resolved.`);
80
+ }
81
+ catch (err) {
82
+ (0, ui_1.handleError)(err);
83
+ }
84
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.foldersListCommand = foldersListCommand;
4
+ exports.foldersCreateCommand = foldersCreateCommand;
5
+ exports.foldersRenameCommand = foldersRenameCommand;
6
+ exports.foldersDeleteCommand = foldersDeleteCommand;
7
+ exports.foldersMoveCommand = foldersMoveCommand;
8
+ const dashboardApi_1 = require("../lib/dashboardApi");
9
+ const ui_1 = require("../lib/ui");
10
+ /** `cku folders` — agent folders, on the dashboard session. */
11
+ async function foldersListCommand(opts) {
12
+ try {
13
+ const res = await (0, dashboardApi_1.dashboardApi)().agentsWithFolders();
14
+ const folders = res?.agentFolders || res?.folders || [];
15
+ const agents = res?.agents || [];
16
+ const rows = folders.map((f) => ({ id: f._id, name: f.name, agents: agents.filter((a) => a.folderId === f._id).length }));
17
+ if (opts.json)
18
+ return void console.log(JSON.stringify(rows, null, 2));
19
+ if (!rows.length)
20
+ return void (0, ui_1.info)('No agent folders.');
21
+ (0, ui_1.table)(['ID', 'NAME', 'AGENTS'], rows.map((r) => [r.id, r.name, r.agents]));
22
+ }
23
+ catch (err) {
24
+ (0, ui_1.handleError)(err);
25
+ }
26
+ }
27
+ async function foldersCreateCommand(name, opts) {
28
+ try {
29
+ const res = await (0, dashboardApi_1.dashboardApi)().createAgentFolder(name.trim());
30
+ if (opts.json)
31
+ return void console.log(JSON.stringify(res, null, 2));
32
+ (0, ui_1.success)(`Created folder "${name.trim()}".`);
33
+ }
34
+ catch (err) {
35
+ (0, ui_1.handleError)(err);
36
+ }
37
+ }
38
+ async function foldersRenameCommand(folderId, name, opts) {
39
+ try {
40
+ const res = await (0, dashboardApi_1.dashboardApi)().renameAgentFolder(folderId, name.trim());
41
+ if (opts.json)
42
+ return void console.log(JSON.stringify(res, null, 2));
43
+ (0, ui_1.success)(`Renamed folder ${folderId} to "${name.trim()}".`);
44
+ }
45
+ catch (err) {
46
+ (0, ui_1.handleError)(err);
47
+ }
48
+ }
49
+ async function foldersDeleteCommand(folderId, opts) {
50
+ try {
51
+ if (!(await (0, ui_1.confirm)(`Delete folder ${folderId}?`, opts.yes)))
52
+ return;
53
+ const res = await (0, dashboardApi_1.dashboardApi)().deleteAgentFolder(folderId);
54
+ if (opts.json)
55
+ return void console.log(JSON.stringify(res, null, 2));
56
+ (0, ui_1.success)(`Deleted folder ${folderId}.`);
57
+ }
58
+ catch (err) {
59
+ (0, ui_1.handleError)(err);
60
+ }
61
+ }
62
+ async function foldersMoveCommand(folderId, opts) {
63
+ try {
64
+ const ids = (opts.agents || '').split(',').map((s) => s.trim()).filter(Boolean);
65
+ if (!ids.length)
66
+ throw new Error('--agents <id,id,...> is required.');
67
+ const res = await (0, dashboardApi_1.dashboardApi)().moveAgentsToFolder(ids, folderId);
68
+ if (opts.json)
69
+ return void console.log(JSON.stringify(res, null, 2));
70
+ (0, ui_1.success)(`Moved ${ids.length} agent(s) to folder ${folderId}.`);
71
+ }
72
+ catch (err) {
73
+ (0, ui_1.handleError)(err);
74
+ }
75
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.knowledgeListCommand = knowledgeListCommand;
4
+ exports.knowledgeDeleteCommand = knowledgeDeleteCommand;
5
+ const dashboardApi_1 = require("../lib/dashboardApi");
6
+ const ui_1 = require("../lib/ui");
7
+ async function knowledgeListCommand(opts) {
8
+ try {
9
+ const res = await (0, dashboardApi_1.dashboardApi)().listKnowledge();
10
+ if (opts.json)
11
+ return void console.log(JSON.stringify(res, null, 2));
12
+ const rows = res?.data || res?.knowledge || (Array.isArray(res) ? res : []);
13
+ if (!rows.length)
14
+ return void (0, ui_1.info)('No knowledge base items.');
15
+ (0, ui_1.table)(['ID', 'NAME', 'TYPE', 'CREATED'], rows.map((r) => [r._id || r.id || '', r.name || r.title || '', r.type || '', r.createdAt || '']));
16
+ }
17
+ catch (err) {
18
+ (0, ui_1.handleError)(err);
19
+ }
20
+ }
21
+ async function knowledgeDeleteCommand(id, opts) {
22
+ try {
23
+ if (!(await (0, ui_1.confirm)(`Delete knowledge item ${id}? This cannot be undone.`, opts.yes)))
24
+ return;
25
+ const res = await (0, dashboardApi_1.dashboardApi)().deleteKnowledge(id);
26
+ if (opts.json)
27
+ return void console.log(JSON.stringify(res ?? { id }, null, 2));
28
+ (0, ui_1.success)(`Deleted ${id}.`);
29
+ }
30
+ catch (err) {
31
+ (0, ui_1.handleError)(err);
32
+ }
33
+ }
@@ -22,6 +22,18 @@ exports.kpiReportAddVarCommand = kpiReportAddVarCommand;
22
22
  exports.kpiReportRemoveVarCommand = kpiReportRemoveVarCommand;
23
23
  exports.kpiReportAddElementCommand = kpiReportAddElementCommand;
24
24
  exports.kpiReportRemoveElementCommand = kpiReportRemoveElementCommand;
25
+ exports.kpiImportCommand = kpiImportCommand;
26
+ exports.kpiDetachCommand = kpiDetachCommand;
27
+ exports.kpiRcaReportsCommand = kpiRcaReportsCommand;
28
+ exports.kpiRcaTimelineCommand = kpiRcaTimelineCommand;
29
+ exports.kpiRcaPdfCommand = kpiRcaPdfCommand;
30
+ exports.kpiRcaDraftPromptCommand = kpiRcaDraftPromptCommand;
31
+ exports.kpiSourceAddApiCommand = kpiSourceAddApiCommand;
32
+ exports.kpiSourceAddPythonCommand = kpiSourceAddPythonCommand;
33
+ exports.kpiSourceAddExprCommand = kpiSourceAddExprCommand;
34
+ exports.kpiSourceRemoveCommand = kpiSourceRemoveCommand;
35
+ exports.kpiMetricSetCommand = kpiMetricSetCommand;
36
+ exports.kpiMetricRemoveCommand = kpiMetricRemoveCommand;
25
37
  const fs_1 = require("fs");
26
38
  const dashboardApi_1 = require("../lib/dashboardApi");
27
39
  const setJson_1 = require("./setJson");
@@ -348,7 +360,11 @@ async function kpiHistoryCommand(opts) {
348
360
  if (!agentIds.length) {
349
361
  return (0, ui_1.handleError)(new Error(`No agent ids in --agents "${opts.agents}".\n Use a comma-separated list, e.g. --agents abc123,def456`));
350
362
  }
351
- const res = await (0, dashboardApi_1.dashboardApi)().kpiHistory({ agentIds: agentIds.join(',') });
363
+ const res = await (0, dashboardApi_1.dashboardApi)().kpiHistory({
364
+ agentIds: agentIds.join(','),
365
+ startDate: opts.since ? dateBound(opts.since, false) : undefined,
366
+ endDate: opts.until ? dateBound(opts.until, true) : undefined,
367
+ });
352
368
  if (opts.json)
353
369
  return void console.log(JSON.stringify(res, null, 2));
354
370
  // Deliberately generic: this response's shape was not confirmed field by field,
@@ -494,6 +510,18 @@ async function kpiCommentCommand(taskId, text, opts = {}) {
494
510
  */
495
511
  async function kpiAssignCommand(taskId, opts) {
496
512
  try {
513
+ if (opts.unassign) {
514
+ const res = await (0, dashboardApi_1.dashboardApi)().savePendingAssignments([
515
+ { taskId, unassign: true, versionIds: splitList(opts.versions), versionNames: splitList(opts.versionNames) },
516
+ ]);
517
+ const result = Array.isArray(res?.results) ? res.results[0] : undefined;
518
+ if (result && result.success === false) {
519
+ return (0, ui_1.handleError)(new Error(result.error || 'The server refused the unassignment.'));
520
+ }
521
+ if (opts.json)
522
+ return void console.log(JSON.stringify(res, null, 2));
523
+ return void (0, ui_1.success)(`Unassigned ${taskId}.`);
524
+ }
497
525
  let to = String(opts.to || '').trim();
498
526
  if (!to) {
499
527
  return (0, ui_1.handleError)(new Error('--to <who> is required.\n' +
@@ -1042,3 +1070,236 @@ async function kpiReportRemoveElementCommand(agentId, elementId, opts) {
1042
1070
  (0, ui_1.handleError)(err);
1043
1071
  }
1044
1072
  }
1073
+ // --- import / detach / RCA reports / data sources -----------------------------
1074
+ /** `cku kpi import --file <rows.json>` — bulk-create cards. */
1075
+ async function kpiImportCommand(opts) {
1076
+ try {
1077
+ const raw = (0, setJson_1.readSetJson)(opts.file.startsWith('@') ? opts.file : '@' + opts.file);
1078
+ const list = Array.isArray(raw) ? raw : raw?.rows;
1079
+ if (!Array.isArray(list) || !list.length) {
1080
+ throw new Error('File must hold a non-empty JSON array of task rows, or {"rows": [...]}.');
1081
+ }
1082
+ const rows = list.map((r) => (r && r.columnId ? { ...r, columnId: columnId(r.columnId) } : r));
1083
+ const res = await (0, dashboardApi_1.dashboardApi)().importPendingTasks(rows);
1084
+ if (opts.json)
1085
+ return void console.log(JSON.stringify(res, null, 2));
1086
+ (0, ui_1.success)(`Imported ${res?.created ?? 0} card(s)${res?.skipped?.length ? ` (${res.skipped.length} skipped)` : ''}.`);
1087
+ }
1088
+ catch (err) {
1089
+ (0, ui_1.handleError)(err);
1090
+ }
1091
+ }
1092
+ /** `cku kpi detach <taskId> <url>` — remove one attachment. */
1093
+ async function kpiDetachCommand(taskId, url, opts) {
1094
+ try {
1095
+ const res = await (0, dashboardApi_1.dashboardApi)().deletePendingTaskAttachment(taskId, url);
1096
+ if (opts.json)
1097
+ return void console.log(JSON.stringify(res ?? { taskId, url }, null, 2));
1098
+ (0, ui_1.success)(`Detached attachment from ${taskId}.`);
1099
+ }
1100
+ catch (err) {
1101
+ (0, ui_1.handleError)(err);
1102
+ }
1103
+ }
1104
+ /** `cku kpi rca-reports` — daywise RCA reports. */
1105
+ async function kpiRcaReportsCommand(opts) {
1106
+ try {
1107
+ const res = await (0, dashboardApi_1.dashboardApi)().daywiseRcaReports({
1108
+ agentId: opts.agent,
1109
+ startDate: opts.since ? dateBound(opts.since, false) : undefined,
1110
+ endDate: opts.until ? dateBound(opts.until, true) : undefined,
1111
+ source: opts.source,
1112
+ page: Number(opts.page) || 1,
1113
+ pageSize: pageSize(opts.limit, 20),
1114
+ });
1115
+ if (opts.json)
1116
+ return void console.log(JSON.stringify(res?.data || res, null, 2));
1117
+ const rows = Array.isArray(res?.data) ? res.data : [];
1118
+ if (!rows.length)
1119
+ return void (0, ui_1.info)('No RCA reports.');
1120
+ (0, ui_1.table)(['DATE', 'AGENT', 'SOURCE', 'AUDITED', 'ROOT CAUSES', 'CONFIDENCE', 'GAPS'], rows.map((r) => [
1121
+ r.rcaDate || r.date || '',
1122
+ r.agentName || r.agentId || '',
1123
+ r.source || r.reportSource || '',
1124
+ r.auditedCalls ?? r.audited ?? '',
1125
+ r.rootCauseCount ?? '',
1126
+ r.confidence ?? '',
1127
+ Array.isArray(r.metricGaps) && r.metricGaps.length
1128
+ ? r.metricGaps.map((g) => `${g.metricName}: ${g.actualPct}% vs ${g.targetPct}% (${g.gapPct > 0 ? '+' : ''}${g.gapPct}pp)`).join('; ')
1129
+ : '',
1130
+ ]));
1131
+ }
1132
+ catch (err) {
1133
+ (0, ui_1.handleError)(err);
1134
+ }
1135
+ }
1136
+ /** `cku kpi rca-timeline <agentId>` — day-by-day RCA execution events. */
1137
+ async function kpiRcaTimelineCommand(agentId, opts) {
1138
+ try {
1139
+ const res = await (0, dashboardApi_1.dashboardApi)().daywiseRcaTimeline(agentId, {
1140
+ startDate: opts.since ? dateBound(opts.since, false) : undefined,
1141
+ endDate: opts.until ? dateBound(opts.until, true) : undefined,
1142
+ });
1143
+ if (opts.json)
1144
+ return void console.log(JSON.stringify(res?.data || res, null, 2));
1145
+ const days = res?.data?.days || [];
1146
+ if (!days.length)
1147
+ return void (0, ui_1.info)('No timeline events.');
1148
+ for (const day of days) {
1149
+ (0, ui_1.info)(`\n${day.date || day.rcaDate || ''}`);
1150
+ (0, ui_1.table)(['TIME', 'STAGE', 'STEP', 'STATUS', 'TITLE', 'DETAIL'], (day.events || []).map((e) => [e.at || '', e.stage || '', e.step || '', e.status || '', clip(e.title, 40), clip(e.detail, 60)]));
1151
+ }
1152
+ }
1153
+ catch (err) {
1154
+ (0, ui_1.handleError)(err);
1155
+ }
1156
+ }
1157
+ /** `cku kpi rca-pdf <agentId> <date>` — download one RCA report PDF. */
1158
+ async function kpiRcaPdfCommand(agentId, rcaDate, opts) {
1159
+ try {
1160
+ const { bytes, filename } = await (0, dashboardApi_1.dashboardApi)().daywiseRcaPdf(agentId, rcaDate, opts.manual ? 'manual' : undefined);
1161
+ const file = opts.file || filename || `rca_${agentId}_${rcaDate}.pdf`;
1162
+ (0, fs_1.writeFileSync)(file, bytes);
1163
+ (0, ui_1.success)(`Wrote ${file} (${bytes.length} bytes).`);
1164
+ }
1165
+ catch (err) {
1166
+ (0, ui_1.handleError)(err);
1167
+ }
1168
+ }
1169
+ /** `cku kpi rca-draft-prompt <agentId> --prompt <text>` — AI-draft the RCA prompt. */
1170
+ async function kpiRcaDraftPromptCommand(agentId, opts) {
1171
+ try {
1172
+ const current = await loadKpiConfig(agentId);
1173
+ const res = await (0, dashboardApi_1.dashboardApi)().generateRcaPromptDraft({
1174
+ agentId,
1175
+ text_input: opts.prompt,
1176
+ ...(current.rca_prompt ? { existingRcaPrompt: current.rca_prompt } : {}),
1177
+ ...(current.kpi_metrics ? { kpi_metrics: current.kpi_metrics } : {}),
1178
+ });
1179
+ const draft = res?.response || res?.data || '';
1180
+ if (opts.save) {
1181
+ return void (await saveKpiConfig(agentId, current, { rca_prompt: String(draft) }, opts.json, 'Saved generated RCA prompt.'));
1182
+ }
1183
+ if (opts.json)
1184
+ return void console.log(JSON.stringify(res, null, 2));
1185
+ console.log(typeof draft === 'string' ? draft : JSON.stringify(draft, null, 2));
1186
+ }
1187
+ catch (err) {
1188
+ (0, ui_1.handleError)(err);
1189
+ }
1190
+ }
1191
+ const PHONE_PARAM = { key_name: 'phone_number', value: 'user_phone_number' };
1192
+ /** Parameters with the mandatory phone_number entry first (bundle's `PXe`). */
1193
+ function withPhoneParam(raw) {
1194
+ const extra = raw ? (0, setJson_1.readSetJson)(raw) : [];
1195
+ if (!Array.isArray(extra))
1196
+ throw new Error('--params must be a JSON array of {key_name, value}.');
1197
+ return [PHONE_PARAM, ...extra.filter((p) => p?.key_name !== 'phone_number')];
1198
+ }
1199
+ /** Replace the entry with the same name, or append. */
1200
+ function upsertByName(list, item) {
1201
+ const out = [...(list || [])];
1202
+ const i = out.findIndex((x) => x?.name === item.name);
1203
+ if (i >= 0)
1204
+ out[i] = item;
1205
+ else
1206
+ out.push(item);
1207
+ return out;
1208
+ }
1209
+ /** `cku kpi source add-api <agentId>` */
1210
+ async function kpiSourceAddApiCommand(agentId, opts) {
1211
+ try {
1212
+ if (!opts.name?.trim())
1213
+ throw new Error('--name is required.');
1214
+ if (!opts.url?.trim())
1215
+ throw new Error('--url is required.');
1216
+ const headers = opts.headers ? (0, setJson_1.readSetJson)(opts.headers) : {};
1217
+ const current = await loadKpiConfig(agentId);
1218
+ const item = { name: opts.name.trim(), url: opts.url.trim(), method: 'POST', headers, parameters: withPhoneParam(opts.params) };
1219
+ await saveKpiConfig(agentId, current, { api_endpoints: upsertByName(current.api_endpoints, item) }, opts.json, `API data source ${item.name} saved.`);
1220
+ }
1221
+ catch (err) {
1222
+ (0, ui_1.handleError)(err);
1223
+ }
1224
+ }
1225
+ /** `cku kpi source add-python <agentId>` */
1226
+ async function kpiSourceAddPythonCommand(agentId, opts) {
1227
+ try {
1228
+ if (!opts.name?.trim())
1229
+ throw new Error('--name is required.');
1230
+ if (!opts.code)
1231
+ throw new Error('--code is required (inline, or @file.py).');
1232
+ const code = opts.code.startsWith('@') ? (0, fs_1.readFileSync)(opts.code.slice(1), 'utf8') : opts.code;
1233
+ if (!opts.skipVerify)
1234
+ await (0, dashboardApi_1.dashboardApi)().verifyKpiPythonCode(code);
1235
+ const current = await loadKpiConfig(agentId);
1236
+ const item = { name: opts.name.trim(), source_code: code, parameters: withPhoneParam(opts.params) };
1237
+ await saveKpiConfig(agentId, current, { python_data_sources: upsertByName(current.python_data_sources, item) }, opts.json, `Python data source ${item.name} saved.`);
1238
+ }
1239
+ catch (err) {
1240
+ (0, ui_1.handleError)(err);
1241
+ }
1242
+ }
1243
+ /** `cku kpi source add-expr <agentId>` */
1244
+ async function kpiSourceAddExprCommand(agentId, opts) {
1245
+ try {
1246
+ if (!/^[A-Za-z][A-Za-z0-9_-]{0,63}$/.test(opts.name || '')) {
1247
+ throw new Error('--name must start with a letter, then letters, digits, _ or -, max 64 chars.');
1248
+ }
1249
+ const item = {
1250
+ name: opts.name,
1251
+ mode: opts.conditions ? 'conditions' : 'expression',
1252
+ conditions: opts.conditions ? (0, setJson_1.readSetJson)(opts.conditions) : [],
1253
+ expression: opts.expression || '',
1254
+ equals: true,
1255
+ consider_for: opts.considerFor === 'unique' ? 'unique' : 'all',
1256
+ };
1257
+ const current = await loadKpiConfig(agentId);
1258
+ await saveKpiConfig(agentId, current, { expression_data_sources: upsertByName(current.expression_data_sources, item) }, opts.json, `Expression data source ${item.name} saved.`);
1259
+ }
1260
+ catch (err) {
1261
+ (0, ui_1.handleError)(err);
1262
+ }
1263
+ }
1264
+ /** `cku kpi source remove <agentId> <name>` */
1265
+ async function kpiSourceRemoveCommand(agentId, name, opts) {
1266
+ try {
1267
+ const current = await loadKpiConfig(agentId);
1268
+ const drop = (l) => (l || []).filter((x) => x?.name !== name);
1269
+ await saveKpiConfig(agentId, current, {
1270
+ api_endpoints: drop(current.api_endpoints),
1271
+ python_data_sources: drop(current.python_data_sources),
1272
+ expression_data_sources: drop(current.expression_data_sources),
1273
+ }, opts.json, `Data source ${name} removed.`);
1274
+ }
1275
+ catch (err) {
1276
+ (0, ui_1.handleError)(err);
1277
+ }
1278
+ }
1279
+ /** `cku kpi metric set <agentId>` */
1280
+ async function kpiMetricSetCommand(agentId, opts) {
1281
+ try {
1282
+ if (!opts.name?.trim() || !opts.source?.trim() || opts.value === undefined || String(opts.value).trim() === '') {
1283
+ throw new Error('--name, --source and --value are all required.');
1284
+ }
1285
+ const current = await loadKpiConfig(agentId);
1286
+ const item = { name: opts.name.trim(), source: opts.source.trim(), value: String(opts.value) };
1287
+ await saveKpiConfig(agentId, current, { kpi_metrics: upsertByName(current.kpi_metrics, item) }, opts.json, `Metric ${item.name} saved.`);
1288
+ }
1289
+ catch (err) {
1290
+ (0, ui_1.handleError)(err);
1291
+ }
1292
+ }
1293
+ /** `cku kpi metric remove <agentId> <name>` */
1294
+ async function kpiMetricRemoveCommand(agentId, name, opts) {
1295
+ try {
1296
+ if (name === 'connected_to_conversion_percentage') {
1297
+ throw new Error('connected_to_conversion_percentage is locked by the server and cannot be removed. Change it with: cku kpi target');
1298
+ }
1299
+ const current = await loadKpiConfig(agentId);
1300
+ await saveKpiConfig(agentId, current, { kpi_metrics: (current.kpi_metrics || []).filter((m) => m?.name !== name) }, opts.json, `Metric ${name} removed.`);
1301
+ }
1302
+ catch (err) {
1303
+ (0, ui_1.handleError)(err);
1304
+ }
1305
+ }