callkaro-cli-unofficial 0.1.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.
Files changed (42) hide show
  1. package/README.md +196 -0
  2. package/dist/commands/accounts.js +275 -0
  3. package/dist/commands/agentPhone.js +50 -0
  4. package/dist/commands/agentVersions.js +228 -0
  5. package/dist/commands/agents.js +455 -0
  6. package/dist/commands/analytics.js +77 -0
  7. package/dist/commands/analyzers.js +130 -0
  8. package/dist/commands/auditStrategies.js +178 -0
  9. package/dist/commands/batches.js +189 -0
  10. package/dist/commands/calls.js +193 -0
  11. package/dist/commands/chatAgents.js +251 -0
  12. package/dist/commands/jsonInput.js +56 -0
  13. package/dist/commands/kpi.js +681 -0
  14. package/dist/commands/models.js +99 -0
  15. package/dist/commands/numbers.js +154 -0
  16. package/dist/commands/ongoing.js +39 -0
  17. package/dist/commands/secrets.js +124 -0
  18. package/dist/commands/setJson.js +42 -0
  19. package/dist/commands/simulations.js +139 -0
  20. package/dist/commands/skills.js +318 -0
  21. package/dist/commands/transcribers.js +95 -0
  22. package/dist/commands/update.js +86 -0
  23. package/dist/commands/voices.js +211 -0
  24. package/dist/data/catalog.json +1978 -0
  25. package/dist/endpoints.json +8 -0
  26. package/dist/index.js +684 -0
  27. package/dist/lib/agentImport.js +60 -0
  28. package/dist/lib/agentUpdate.js +262 -0
  29. package/dist/lib/api.js +287 -0
  30. package/dist/lib/browserAuth.js +93 -0
  31. package/dist/lib/catalog.js +136 -0
  32. package/dist/lib/config.js +354 -0
  33. package/dist/lib/dashboardApi.js +197 -0
  34. package/dist/lib/dashboardAuth.js +339 -0
  35. package/dist/lib/dbFields.js +30 -0
  36. package/dist/lib/logQuery.js +140 -0
  37. package/dist/lib/looseJson.js +198 -0
  38. package/dist/lib/session.js +17 -0
  39. package/dist/lib/types.js +15 -0
  40. package/dist/lib/ui.js +91 -0
  41. package/dist/lib/versions.js +19 -0
  42. package/package.json +52 -0
package/dist/index.js ADDED
@@ -0,0 +1,684 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const ui_1 = require("./lib/ui");
10
+ const accounts_1 = require("./commands/accounts");
11
+ const dashboardAuth_1 = require("./lib/dashboardAuth");
12
+ const kpi_1 = require("./commands/kpi");
13
+ /** Board columns, for `--help`. Ids are what the server keys on, not labels. */
14
+ const KPI_COLUMNS = 'action_items | in_progress | testing_ab | live_prod | done | rejected';
15
+ const analyzers_1 = require("./commands/analyzers");
16
+ const models_1 = require("./commands/models");
17
+ const agents_1 = require("./commands/agents");
18
+ const agentVersions_1 = require("./commands/agentVersions");
19
+ const agentPhone_1 = require("./commands/agentPhone");
20
+ const numbers_1 = require("./commands/numbers");
21
+ const calls_1 = require("./commands/calls");
22
+ const ongoing_1 = require("./commands/ongoing");
23
+ const transcribers_1 = require("./commands/transcribers");
24
+ const voices_1 = require("./commands/voices");
25
+ const secrets_1 = require("./commands/secrets");
26
+ const skills_1 = require("./commands/skills");
27
+ const update_1 = require("./commands/update");
28
+ const analytics_1 = require("./commands/analytics");
29
+ const batches_1 = require("./commands/batches");
30
+ const simulations_1 = require("./commands/simulations");
31
+ const auditStrategies_1 = require("./commands/auditStrategies");
32
+ const chatAgents_1 = require("./commands/chatAgents");
33
+ // The command this fork installs as. Deliberately NOT `ck` or `callkaro`:
34
+ // those belong to @callkaro-official/cli, and both packages write to the same
35
+ // global node_modules/.bin, so sharing a name means whichever npm install ran
36
+ // last silently wins. `cku` cannot collide.
37
+ const name = 'cku';
38
+ const { version: VERSION } = require('../package.json');
39
+ const program = new commander_1.Command();
40
+ program
41
+ .name(name)
42
+ .description('CallKaro Unofficial CLI — manage your voice AI agents from the terminal')
43
+ .version(VERSION, '-v, --version', 'show version')
44
+ .helpOption('-h, --help', 'show help')
45
+ .showHelpAfterError(chalk_1.default.dim(`(run "${name} --help" for usage)`))
46
+ .showSuggestionAfterError()
47
+ .addHelpText('before', (0, ui_1.banner)(VERSION));
48
+ // --- auth ------------------------------------------------------------------
49
+ program
50
+ .command('register')
51
+ .description('create an account (Google, or email + password + OTP)')
52
+ .option('--google', 'sign up with Google in your browser (skips the picker)')
53
+ .option('--email <email>', 'email address')
54
+ .option('--name <name>', 'your name')
55
+ .action(accounts_1.registerCommand);
56
+ program
57
+ .command('login')
58
+ .description('sign in through your browser (no password in the terminal)')
59
+ .option('--password', 'use the classic email + password prompt instead')
60
+ .option('--email <email>', 'email address (with --password)')
61
+ .action(accounts_1.loginCommand);
62
+ program
63
+ .command('logout')
64
+ .description('revoke this session and clear the stored token')
65
+ .action(accounts_1.logoutCommand);
66
+ program
67
+ .command('whoami')
68
+ .description('show the logged-in account details')
69
+ .option('--json', 'output raw JSON')
70
+ .action(accounts_1.whoamiCommand);
71
+ // Dashboard sign-in. A different credential from the ck_ token above: it reaches
72
+ // /auth/* and /v2/*, not /cli/*. Reproduces the web app's own email+password flow,
73
+ // which never touches Firebase. See lib/dashboardAuth.ts.
74
+ const dashboard = program
75
+ .command('dashboard')
76
+ .description('sign in to the CallKaro web dashboard (a different credential from the ck_ token)');
77
+ dashboard
78
+ .command('login')
79
+ .description('sign in with email + password, exactly as the login form does')
80
+ .option('--email <email>', 'skip the email prompt')
81
+ .action(dashboardAuth_1.dashboardLoginCommand);
82
+ dashboard
83
+ .command('whoami')
84
+ .description('prove the stored dashboard token still works')
85
+ .option('--json', 'output raw JSON')
86
+ .action(dashboardAuth_1.dashboardWhoamiCommand);
87
+ dashboard
88
+ .command('probe')
89
+ .description('GET a set of dashboard routes and print status + shape (read-only)')
90
+ .action(dashboardAuth_1.dashboardProbeCommand);
91
+ dashboard
92
+ .command('logout')
93
+ .description('forget the dashboard session (the ck_ token is kept)')
94
+ .option('--yes', 'skip the confirmation')
95
+ .action(dashboardAuth_1.dashboardLogoutCommand);
96
+ // Which models the server accepts. Same source the validator uses, so it cannot drift.
97
+ program
98
+ .command('models')
99
+ .description('list the models the server accepts, per slot')
100
+ .option('--slot <name>', 'list one slot in full, e.g. agent, post-call, kpi-rca')
101
+ .option('--json', 'output raw JSON')
102
+ .addHelpText('after', '\n Reads the server when you have a dashboard session, otherwise the copy in\n' +
103
+ ' catalog.json last synced from it. Use --slot for one slot in full:\n' +
104
+ ' cku models --slot agent\n')
105
+ .action(models_1.modelsCommand);
106
+ // KPI manager — the pending-task board. Needs a dashboard session.
107
+ const kpi = program
108
+ .command('kpi')
109
+ .description('KPI manager: pending tasks and metric history (needs `cku dashboard login`)');
110
+ kpi
111
+ .command('agents')
112
+ .description('list the agents this account is scored on')
113
+ .option('--json', 'output raw JSON')
114
+ .action(kpi_1.kpiAgentsCommand);
115
+ kpi
116
+ .command('tasks')
117
+ .description('pending tasks, filtered and paged')
118
+ .option('--limit <n>', 'rows to show (default 25)')
119
+ .option('--severity <levels>', 'Very High | High | Medium | Low | Very Low | Negligible (comma = any of)')
120
+ .option('--column <cols>', `board column, comma = any of. ${KPI_COLUMNS}`)
121
+ .option('--agent <value>', 'agent id or name (case-insensitive substring)')
122
+ .option('--since <date>', 'rcaDate on or after: YYYY-MM-DD, 7d, 30d, today, thisweek')
123
+ .option('--until <date>', 'rcaDate on or before: same formats')
124
+ .option('--assigned <who>', 'assignee name, or "none" for unassigned')
125
+ .option('--source <src>', 'report source, e.g. nightly')
126
+ .option('--search <text>', 'substring of cause, fix or agent')
127
+ .option('--has-comments', 'only tasks that have comments')
128
+ .option('--with-calls', 'only tasks with attached call ids')
129
+ .option('--json', 'output every match as raw JSON (not paged)')
130
+ .action(kpi_1.kpiTasksCommand);
131
+ kpi
132
+ .command('create')
133
+ .description('add a card to the board')
134
+ .requiredOption('--cause <text>', 'what went wrong — the one field the server requires')
135
+ .option('--fix <text>', 'recommended fix')
136
+ .option('--description <text>', 'longer context for the card')
137
+ .option('--severity <level>', 'Very High | High | Medium | Low | Very Low | Negligible')
138
+ .option('--column <col>', `start column (default action_items). One of: ${KPI_COLUMNS}`)
139
+ .option('--impact <n>', 'estimated impact, a percentage')
140
+ .option('--calls <ids>', 'comma-separated call ids to attach')
141
+ .action(kpi_1.kpiCreateCommand);
142
+ kpi
143
+ .command('move')
144
+ .description('move a card to another column — this is also how you retire one')
145
+ .argument('<taskId>', 'task id, e.g. <agentId>:<date>:<n> — quote it in shells')
146
+ .argument('<column>', `target column: ${KPI_COLUMNS}`)
147
+ .option('--json', 'output raw JSON')
148
+ .addHelpText('after', '\n There is no delete. "rejected" retires a card without destroying it.\n')
149
+ .action(kpi_1.kpiMoveCommand);
150
+ kpi
151
+ .command('set')
152
+ .description("set a card's description (the only field the board lets you edit)")
153
+ .argument('<taskId>', 'task id — quote it in shells')
154
+ .requiredOption('--description <text>', 'the new description')
155
+ .option('--json', 'output raw JSON')
156
+ .addHelpText('after', '\n cause, --fix and --severity are fixed at create. To correct one, create a\n' +
157
+ ' replacement and move the original to rejected.\n')
158
+ .action(kpi_1.kpiSetCommand);
159
+ kpi
160
+ .command('comment')
161
+ .description('append a comment to a card')
162
+ .argument('<taskId>', 'task id — quote it in shells')
163
+ .argument('<text>', 'the comment')
164
+ .option('--json', 'output raw JSON')
165
+ .action(kpi_1.kpiCommentCommand);
166
+ kpi
167
+ .command('assignees')
168
+ .description('list the people a card can be assigned to')
169
+ .option('--json', 'output raw JSON')
170
+ .action(kpi_1.kpiAssigneesCommand);
171
+ kpi
172
+ .command('assign')
173
+ .description('assign a card to a person, or to AI FDE')
174
+ .argument('<taskId>', 'task id — quote it in shells')
175
+ .requiredOption('--to <who>', 'team member id or name, or "ai_fde"')
176
+ .option('--agent <id>', 'agent to change (required for ai_fde)')
177
+ .option('--versions <ids>', 'destination version ids (required for ai_fde)')
178
+ .option('--version-names <names>', 'display names for the version ids')
179
+ .option('--ab-split', 'apply across the A/B split')
180
+ .option('--apply-to-published', 'also apply to the published version')
181
+ .option('--json', 'output raw JSON')
182
+ .action(kpi_1.kpiAssignCommand);
183
+ kpi
184
+ .command('target')
185
+ .description("set an agent's KPI Manager target percentage")
186
+ .requiredOption('--agent <id>', 'agent id (see `cku kpi agents`)')
187
+ .requiredOption('--value <n>', 'the target, e.g. 25 for 25%')
188
+ .option('--rca-model <id>', 'model used for RCA runs')
189
+ .option('--rca-prompt <text>', 'RCA prompt')
190
+ .option('--json', 'output raw JSON')
191
+ .addHelpText('after', '\n The server replaces the whole config at once, so an RCA prompt already set on\n' +
192
+ ' the account is read back and re-sent rather than cleared.\n')
193
+ .action(kpi_1.kpiTargetCommand);
194
+ kpi
195
+ .command('rca')
196
+ .description('start an RCA run for one agent over a date range')
197
+ .requiredOption('--agent <id>', 'agent id — one at a time (see `cku kpi agents`)')
198
+ .requiredOption('--since <date>', 'start: YYYY-MM-DD, 7d, 30d, today, thisweek')
199
+ .requiredOption('--until <date>', 'end: same formats')
200
+ .option('--json', 'output raw JSON')
201
+ .action(kpi_1.kpiRcaCommand);
202
+ kpi
203
+ .command('show')
204
+ .description('show one task in full, including its recommended fix')
205
+ .argument('<taskId>', 'task id, e.g. <agentId>:<date>:<n> — quote it in shells')
206
+ .option('--json', 'output raw JSON')
207
+ .action(kpi_1.kpiShowCommand);
208
+ kpi
209
+ .command('history')
210
+ .description('metric history for the given agents')
211
+ .requiredOption('--agents <ids>', 'comma-separated agent ids (see `cku kpi agents`)')
212
+ .option('--json', 'output raw JSON')
213
+ .action(kpi_1.kpiHistoryCommand);
214
+ // Call analyzers and audit reports — also dashboard-side.
215
+ const analyzers = program
216
+ .command('analyzers')
217
+ .description('call analyzers and audit reports (needs `cku dashboard login`)');
218
+ analyzers
219
+ .command('list')
220
+ .description('list call analyzers')
221
+ .option('--json', 'output raw JSON')
222
+ .action(analyzers_1.analyzersListCommand);
223
+ analyzers
224
+ .command('show')
225
+ .description('show one analyzer, including its full instruction')
226
+ .argument('<analyzerId>', 'analyzer id')
227
+ .option('--json', 'output raw JSON')
228
+ .action(analyzers_1.analyzersShowCommand);
229
+ analyzers
230
+ .command('audit-reports')
231
+ .description('completed audit runs')
232
+ .option('--limit <n>', 'rows to show (default 25)')
233
+ .option('--json', 'output raw JSON')
234
+ .action(analyzers_1.analyzersAuditReportsCommand);
235
+ analyzers
236
+ .command('audit-strategies')
237
+ .description('per-strategy audit rollup')
238
+ .option('--json', 'output raw JSON')
239
+ .action(analyzers_1.analyzersAuditStrategiesCommand);
240
+ program
241
+ .command('config')
242
+ .description('show effective CLI settings and session status')
243
+ .action(accounts_1.configCommand);
244
+ // --- multi-account ---------------------------------------------------------
245
+ const accounts = program.command('accounts').description('manage multiple saved logins');
246
+ accounts
247
+ .command('list', { isDefault: true })
248
+ .description('list saved accounts (* = active)')
249
+ .option('--json', 'output raw JSON')
250
+ .action(accounts_1.accountsCommand);
251
+ accounts
252
+ .command('use <email>')
253
+ .description('switch the global default account (shared — avoid for parallel agents)')
254
+ .action(accounts_1.accountsUseCommand);
255
+ accounts
256
+ .command('bind <email>')
257
+ .description('pin an account to this folder via .cku.json (safe for parallel agents)')
258
+ .option('--global', 'set the shared default instead of this folder')
259
+ .action(accounts_1.accountsBindCommand);
260
+ accounts
261
+ .command('unbind')
262
+ .description("remove this folder's .cku.json binding")
263
+ .action(accounts_1.accountsUnbindCommand);
264
+ accounts
265
+ .command('status')
266
+ .description('show which account is in effect and where it came from')
267
+ .action(accounts_1.accountsStatusCommand);
268
+ accounts
269
+ .command('remove <email>')
270
+ .description('forget a saved login')
271
+ .option('--yes', 'skip confirmation')
272
+ .action(accounts_1.accountsRemoveCommand);
273
+ program
274
+ .command('update')
275
+ .description('upgrade the CLI to the latest release, then refresh the skill pack')
276
+ .option('--skip-skills', 'update the CLI only')
277
+ .action(update_1.updateCommand);
278
+ // --- secrets ---------------------------------------------------------------
279
+ const secrets = program.command('secrets').description('manage encrypted account secrets');
280
+ secrets.command('list').description('list secret names and masked values')
281
+ .option('--json', 'output raw JSON with masked values').action(secrets_1.secretsListCommand);
282
+ secrets.command('set <name>').description('create or replace a secret using a hidden prompt')
283
+ .option('--stdin', 'read the secret value from stdin').action(secrets_1.secretsSetCommand);
284
+ secrets.command('remove <name>').description('remove a secret')
285
+ .option('--yes', 'skip confirmation').action(secrets_1.secretsRemoveCommand);
286
+ secrets.command('rename <oldName> <newName>').description('rename a secret without changing its value')
287
+ .action(secrets_1.secretsRenameCommand);
288
+ // --- agents ----------------------------------------------------------------
289
+ const agents = program.command('agents').description('manage voice agents');
290
+ agents.command('list').description('list all your agents')
291
+ .option('--json', 'output raw JSON')
292
+ .option('--file <path>', 'save the agent list as JSON to a file')
293
+ .action(agents_1.agentsListCommand);
294
+ agents.command('get <agentId>').description('show one agent and list its versions')
295
+ .option('--versions <id>', 'version id to load (first id used if a list)')
296
+ .option('--json', 'output raw JSON')
297
+ .option('--file <path>', 'save the agent detail as JSON to a file')
298
+ .action(agents_1.agentsGetCommand);
299
+ agents.command('versions <agentId>').description("list an agent's versions (name, id, language, prompt type, active)")
300
+ .option('--json', 'output raw JSON')
301
+ .option('--file <path>', 'save the version list as JSON to a file')
302
+ .action(agents_1.agentsVersionsCommand);
303
+ agents.command('export <agentId>').description('export agent version(s) as JSON (asks which versions)')
304
+ .option('--versions <ids>', 'comma-separated version IDs (skips the picker)')
305
+ .option('--file <path>', 'output file path (default: <agent>-<timestamp>.json)')
306
+ .action(agents_1.agentsExportCommand);
307
+ agents.command('import <filepath>').description('create an agent from an exported JSON file')
308
+ .option('--dry-run', 'validate the file without creating anything')
309
+ .action(agents_1.agentsImportCommand);
310
+ agents.command('create [json]').description('create an agent from JSON (inline, @file, --file, or stdin)')
311
+ .option('--file <path>', 'read the agent JSON from a file (quote-proof)')
312
+ .action(agents_1.agentsCreateCommand);
313
+ agents.command('update <agentId>').description('update fields via --set \'{json}\'; version fields need --versions')
314
+ .option('--set <json>', "JSON object of fields (or @file.json), e.g. '{\"name\":\"x\",\"temperature\":5}'")
315
+ .option('--versions <id>', 'target a specific version (needed for version-level fields)')
316
+ .option('--commit <msg>', 'commit message for the version change')
317
+ .option('--json', 'output the raw updated-agent response')
318
+ .action(agents_1.agentsUpdateCommand);
319
+ // Phone assignment (agent-centric). The one-inbound-agent rule is enforced server-side.
320
+ agents.command('set-inbound <agentId>').description("add/remove an agent's inbound phone number")
321
+ .option('--number <id>', 'phone number id')
322
+ .option('--remove', 'remove instead of add')
323
+ .action(agentPhone_1.setInboundCommand);
324
+ agents.command('set-outbound <agentId>').description("set/clear an agent's outbound phone number")
325
+ .option('--number <id>', 'phone number id')
326
+ .option('--clear', 'clear the outbound number')
327
+ .action(agentPhone_1.setOutboundCommand);
328
+ // Version management: clone, publish, activate, A/B testing
329
+ agents.command('clone-version <agentId>').description('clone a version with a required target prompt mode and language')
330
+ .option('--versions <id>', 'source version id')
331
+ .option('--name <name>', 'name for the new version')
332
+ .requiredOption('--prompt-type <type>', 'target prompt type: 0, 1, 2, or 3')
333
+ .requiredOption('--language <code>', 'target language: en, hi, kn, ta, te, mr, gu, bn, or ml')
334
+ .option('--set <json>', 'optional voice, transcriber, or silence-language overrides (or @file.json)')
335
+ .option('--json', 'output the raw cloned-version response')
336
+ .action(agentVersions_1.agentsCloneVersionCommand);
337
+ agents.command('publish <agentId>').description('publish a version')
338
+ .option('--versions <id>', 'version id to publish')
339
+ .action(agentVersions_1.agentsPublishCommand);
340
+ agents.command('toggle-active <agentId>').description('activate/deactivate a version')
341
+ .option('--versions <id>', 'version id to toggle')
342
+ .action(agentVersions_1.agentsToggleActiveCommand);
343
+ agents.command('ab <agentId>').description('enable/disable A/B testing with version ratios')
344
+ .option('--versions <spec>', 'weighted versions, e.g. "v1=60,v2=40" (ratios must sum to 100)')
345
+ .option('--disable', 'turn off A/B testing')
346
+ .action(agentVersions_1.agentsAbCommand);
347
+ agents.command('ab-advanced <agentId>').description('rule-based A/B: if/elseif/else on call metadata → version or language')
348
+ .option('--rules <json>', 'rule chain as JSON array, or @rules.json')
349
+ .option('--show', 'print the current rule chain')
350
+ .option('--disable', 'turn off advanced A/B testing')
351
+ .option('--json', 'raw JSON (with --show)')
352
+ .action(agentVersions_1.agentsAbAdvancedCommand);
353
+ // --- chat agents -----------------------------------------------------------
354
+ const chatAgents = program.command('chat-agents').description('manage chat agents');
355
+ chatAgents.command('models').description('list models available for chat agents')
356
+ .option('--json', 'output raw JSON').option('--file <path>', 'save JSON to a file')
357
+ .action(chatAgents_1.chatAgentsModelsCommand);
358
+ chatAgents.command('templates').description('list provider template names and IDs')
359
+ .option('--provider <provider>', 'template provider: meta, aisensy', 'meta')
360
+ .option('--json', 'output raw JSON').option('--file <path>', 'save JSON to a file')
361
+ .action(chatAgents_1.chatAgentsTemplatesCommand);
362
+ chatAgents.command('template <templateId>').description('fetch a provider template by ID')
363
+ .option('--provider <provider>', 'template provider: meta, aisensy', 'meta')
364
+ .option('--json', 'output raw JSON').option('--file <path>', 'save JSON to a file')
365
+ .action(chatAgents_1.chatAgentsTemplateCommand);
366
+ chatAgents.command('list').description('list all chat agents')
367
+ .option('--json', 'output raw JSON').option('--file <path>', 'save JSON to a file')
368
+ .action(chatAgents_1.chatAgentsListCommand);
369
+ chatAgents.command('get <agentId>').description('show a chat agent and its published or selected version')
370
+ .option('--versions <id>', 'version id to load')
371
+ .option('--json', 'output raw JSON').option('--file <path>', 'save JSON to a file')
372
+ .action(chatAgents_1.chatAgentsGetCommand);
373
+ chatAgents.command('versions <agentId>').description("list a chat agent's versions")
374
+ .option('--json', 'output raw JSON').option('--file <path>', 'save JSON to a file')
375
+ .action(chatAgents_1.chatAgentsVersionsCommand);
376
+ chatAgents.command('create [json]').description('create a chat agent from JSON; omitted fields receive safe defaults')
377
+ .option('--file <path>', 'read JSON from a file')
378
+ .option('--json', 'output the raw response')
379
+ .action(chatAgents_1.chatAgentsCreateCommand);
380
+ chatAgents.command('update <agentId>').description('update chat-agent fields from a JSON object')
381
+ .requiredOption('--set <json>', 'JSON object or @file.json')
382
+ .option('--versions <id>', 'target version id')
383
+ .option('--json', 'output the raw response')
384
+ .action(chatAgents_1.chatAgentsUpdateCommand);
385
+ chatAgents.command('create-version <agentId> [json]').description('create a chat-agent version from JSON')
386
+ .option('--file <path>', 'read JSON from a file')
387
+ .option('--json', 'output the raw response')
388
+ .action(chatAgents_1.chatAgentsCreateVersionCommand);
389
+ chatAgents.command('publish <agentId>').description('publish a chat-agent version')
390
+ .requiredOption('--versions <id>', 'version id to publish')
391
+ .option('--json', 'output the raw response')
392
+ .action(chatAgents_1.chatAgentsPublishCommand);
393
+ // --- skills ----------------------------------------------------------------
394
+ const skills = program.command('skills').description('AI skill pack for Claude Code & other agents');
395
+ function addSkillsProviderOptions(command) {
396
+ return command
397
+ .option('--copilot', 'target GitHub Copilot')
398
+ .option('--gemini', 'target Gemini CLI')
399
+ .option('--cursor', 'target Cursor')
400
+ .option('--windsurf', 'target Windsurf')
401
+ .option('--roo', 'target Roo Code')
402
+ .option('--opencode', 'target OpenCode')
403
+ .option('--cline', 'target Cline')
404
+ .option('--claude', 'target Claude Code')
405
+ .option('--codex', 'target OpenAI Codex')
406
+ .option('--all', 'target every supported provider');
407
+ }
408
+ addSkillsProviderOptions(skills.command('install'))
409
+ .description('download the skill pack for one or more AI coding agents')
410
+ .option('--project', 'install into this project instead of your user account')
411
+ .option('--official', "install the upstream pack instead of this fork's")
412
+ .action(skills_1.skillsInstallCommand);
413
+ addSkillsProviderOptions(skills.command('update'))
414
+ .description('re-download the latest skill pack (refreshes every install if no flag given)')
415
+ .option('--project', "update this project's copy only")
416
+ .option('--official', "update from the upstream pack instead of this fork's")
417
+ .action(skills_1.skillsUpdateCommand);
418
+ addSkillsProviderOptions(skills.command('dir'))
419
+ .description('print the selected install location(s)')
420
+ .option('--project', 'print project location(s)')
421
+ .action(skills_1.skillsDirCommand);
422
+ // --- numbers ---------------------------------------------------------------
423
+ const numbers = program.command('numbers').description('manage phone numbers');
424
+ numbers.command('list').description('list your phone numbers').option('--json', 'raw JSON').action(numbers_1.numbersListCommand);
425
+ numbers.command('catalog').description('list buyable pool numbers + price').option('--json', 'raw JSON').action(numbers_1.numbersCatalogCommand);
426
+ numbers.command('buy <numberOrPhone>').description('buy (allot) a pool number (id or phone number)')
427
+ .option('--yes', 'skip confirmation').action(numbers_1.numbersBuyCommand);
428
+ numbers.command('spam <numberOrPhone>').description('mark a number as spam (id or phone number)')
429
+ .option('--off', 'unmark spam instead').action(numbers_1.numbersSpamCommand);
430
+ numbers.command('unspam <numberOrPhone>').description('unmark a number as spam (id or phone number)').action(numbers_1.numbersUnspamCommand);
431
+ numbers.command('release <numberOrPhone>').description('release (unrent) a number (id or phone number)')
432
+ .option('--yes', 'skip confirmation').action(numbers_1.numbersReleaseCommand);
433
+ // Shared Call History filters (mirror the web filter panel). Applied to list + export.
434
+ function addCallFilters(cmd) {
435
+ return cmd
436
+ .option('--type <list>', 'call type(s): inbound,outbound,outbound-api,outbound-scheduled,outbound-test,agent-test')
437
+ .option('--agent <ids>', 'agent id(s), comma-separated')
438
+ .option('--versions <ids>', 'version id(s), comma-separated')
439
+ .option('--start <date>', 'start date (YYYY-MM-DD)')
440
+ .option('--end <date>', 'end date (YYYY-MM-DD)')
441
+ .option('--batch <bool>', 'batch calls only: true|false')
442
+ .option('--hangup <list>', 'hangup reason(s), comma-separated')
443
+ .option('--duration <list>', 'duration bucket(s): 0,1-10,11-30,31-60,>60')
444
+ .option('--to <number>', 'destination number')
445
+ .option('--converted <bool>', 'conversion status: true|false')
446
+ .option('--lead <id>', 'lead id')
447
+ .option('--buylead <value>', 'buylead value');
448
+ }
449
+ /** Collect repeatable --var key=value into an array. */
450
+ const collectVar = (val, acc) => {
451
+ acc.push(val);
452
+ return acc;
453
+ };
454
+ // --- voices / transcribers -------------------------------------------------
455
+ program
456
+ .command('voices')
457
+ .description('list TTS voices, providers and models')
458
+ .option('--provider <p>', 'filter by an account-available provider')
459
+ .option('--model <m>', 'filter by voice model (e.g. sonic-3, bulbul:v3, aura-2)')
460
+ .option('--language <l>', 'filter by language (e.g. hi, en, hi-IN)')
461
+ .option('--gender <g>', 'provider-specific gender (e.g. female or feminine)')
462
+ .option('--query <text>', 'search provider voice names and descriptions')
463
+ .option('--voice-id <id>', 'filter by provider-native voice id')
464
+ .option('--voice-name <name>', 'filter by provider-native voice name')
465
+ .option('--style <style>', 'filter by voice style')
466
+ .option('--locale <locale>', 'filter by provider locale')
467
+ .option('--languages <list>', 'languages, comma-separated')
468
+ .option('--accent <accent>', 'filter by accent')
469
+ .option('--use-cases <list>', 'use cases, comma-separated')
470
+ .option('--category <category>', 'filter by provider category')
471
+ .option('--voice-type <type>', 'filter by voice ownership/type')
472
+ .option('--high-quality', 'only high-quality voices')
473
+ .option('--voice-ids <list>', 'voice ids, comma-separated')
474
+ .option('--collection-id <id>', 'filter by collection id')
475
+ .option('--sort <field>', 'provider sort field')
476
+ .option('--sort-direction <direction>', 'sort direction: asc | desc')
477
+ .option('--is-owner', 'only voices owned by the account')
478
+ .option('--include-archived', 'include archived voices')
479
+ .option('--type <type>', 'provider-specific voice type')
480
+ .option('--project-id <id>', 'filter by project id')
481
+ .option('--id <id>', 'filter by provider-native id')
482
+ .option('--limit <n>', 'maximum voices to return', (value) => Number(value))
483
+ .option('--providers', 'list providers and their models instead of voices')
484
+ .option('--fields', 'show the voice_configuration keys a provider takes (needs --provider)')
485
+ .option('--json', 'raw JSON')
486
+ .action(voices_1.voicesCommand);
487
+ program
488
+ .command('transcribers')
489
+ .description('list transcriber providers, models and languages')
490
+ .option('--provider <p>', 'filter: soniox | deepgram | sarvam | azure | elevenlabs | groq | cartesia | gnani | callkaro')
491
+ .option('--language <l>', 'filter by language (e.g. hi, en, hi-IN)')
492
+ .option('--fields', 'show the transcriber keys a provider takes (needs --provider)')
493
+ .option('--all', 'include admin-gated providers')
494
+ .option('--json', 'raw JSON')
495
+ .action(transcribers_1.transcribersCommand);
496
+ // --- calls -----------------------------------------------------------------
497
+ const calls = program.command('calls').description('calls: place & history');
498
+ calls.command('make').description('place a single outbound call (real, or --test)')
499
+ .option('--agent <id>', 'agent id to call with')
500
+ .option('--to <number>', 'destination phone number')
501
+ .option('--var <k=v>', 'call metadata variable (repeatable)', collectVar, [])
502
+ .option('--metadata <json>', 'metadata as a JSON object')
503
+ .option('--versions <id>', 'use a specific version')
504
+ .option('--commit <id>', 'use a specific version commit')
505
+ .option('--test', 'place a test call (is_test_call=true)')
506
+ .action(calls_1.callsMakeCommand);
507
+ addCallFilters(calls.command('list').description('list call history'))
508
+ .option('--limit <n>', 'page size (default 50)')
509
+ .option('--json', 'raw JSON')
510
+ .action(calls_1.callsListCommand);
511
+ calls.command('get <callId>').description('show one call (with recording URL)')
512
+ .option('--json', 'raw JSON')
513
+ .action(calls_1.callsGetCommand);
514
+ calls.command('logs <callId> [query]')
515
+ .description("run a shell-style query over a call's raw log (grep/awk/sed/cut/sort/uniq/head/tail/wc, piped). " +
516
+ 'Default: grep -v _trace.py /call.log | tail -100. Example: cku calls logs <id> "grep -iE \'error|warning\' /call.log"')
517
+ .action(calls_1.callsLogsCommand);
518
+ addCallFilters(calls.command('export').description('export call history (same filters as the web export)'))
519
+ .option('--fields <list>', 'export_fields — comma-separated columns to include')
520
+ .option('--include-chat', 'include full chat transcript in the export')
521
+ .option('--file <path>', 'output file (default: calls-export-<ts>.csv)')
522
+ .option('--email <email>', 'email the export instead (for large datasets)')
523
+ .action(calls_1.callsExportCommand);
524
+ // --- ongoing ---------------------------------------------------------------
525
+ const ongoing = program.command('ongoing').description('ongoing/live call queues');
526
+ ongoing.command('status').description('live queue counts per agent').option('--json', 'raw JSON').action(ongoing_1.ongoingStatusCommand);
527
+ ongoing.command('pause').description('pause dialing (all, or --agent)').option('--agent <id>', 'one agent only')
528
+ .action((o) => (0, ongoing_1.ongoingControlCommand)('pause', o));
529
+ ongoing.command('resume').description('resume dialing (all, or --agent)').option('--agent <id>', 'one agent only')
530
+ .action((o) => (0, ongoing_1.ongoingControlCommand)('resume', o));
531
+ ongoing.command('clear').description('clear the queue (all, or --agent)').option('--agent <id>', 'one agent only')
532
+ .action((o) => (0, ongoing_1.ongoingControlCommand)('clear', o));
533
+ // --- analytics -------------------------------------------------------------
534
+ const analytics = program.command('analytics').description('analytics & performance');
535
+ analytics.command('overview').description('overview metrics (mirrors the Analytics page filters)')
536
+ .option('--agent <ids>', 'agent id(s), comma-separated')
537
+ .option('--versions <ids>', 'version id(s), comma-separated')
538
+ .option('--type <list>', 'call type(s)')
539
+ .option('--voice-provider <name>', 'filter by voice provider')
540
+ .option('--llm-model <name>', 'filter by LLM model')
541
+ .option('--connected <bool>', 'connected only: true|false')
542
+ .option('--batch <bool>', 'batch calls only: true|false')
543
+ .option('--start <date>', 'start date (YYYY-MM-DD)')
544
+ .option('--end <date>', 'end date (YYYY-MM-DD)')
545
+ .option('--json', 'raw JSON')
546
+ .action(analytics_1.analyticsOverviewCommand);
547
+ analytics.command('performance').description('per-agent performance')
548
+ .option('--start <date>', 'start date')
549
+ .option('--end <date>', 'end date')
550
+ .option('--json', 'raw JSON')
551
+ .action(analytics_1.analyticsPerformanceCommand);
552
+ analytics.command('version <agentId>').description('per-version analytics for an agent')
553
+ .option('--versions <ids>', 'comma-separated version ids')
554
+ .option('--start <date>', 'start date')
555
+ .option('--end <date>', 'end date')
556
+ .option('--json', 'raw JSON')
557
+ .action(analytics_1.analyticsVersionCommand);
558
+ // --- batches ---------------------------------------------------------------
559
+ const batches = program.command('batches').description('batch calls');
560
+ batches.command('create').description('create a batch from a local CSV (uploads + starts dialing)')
561
+ .option('--file <path>', 'path to the CSV of leads')
562
+ .option('--name <name>', 'batch name')
563
+ .option('--agent <id>', 'agent id (omit if CSV has an x_agent_id column)')
564
+ .option('--language <lang>', 'batch language')
565
+ .option('--funnel <list>', 'funnel metric names, comma-separated')
566
+ .option('--workflow <id>', 'workflow id')
567
+ .action(batches_1.batchesCreateCommand);
568
+ batches.command('schedule').description('schedule a batch for later (like the web Schedule flow)')
569
+ .option('--file <path>', 'path to the CSV of leads')
570
+ .option('--name <name>', 'batch name')
571
+ .option('--at <datetime>', 'when to dial (ISO, e.g. "2026-08-05T10:00:00+05:30"); rows may override via x_schedule_at')
572
+ .option('--agent <id>', 'agent id (omit if CSV has an x_agent_id column)')
573
+ .option('--window <HH:MM-HH:MM>', 'daily calling window (default 00:00-23:59)')
574
+ .option('--retries <n>', 'retries for unanswered numbers (default 0)')
575
+ .option('--gaps <mins>', 'gap between retries, comma-separated minutes, e.g. 30,60')
576
+ .option('--carry-over', 'carry unfinished calls to the next day')
577
+ .option('--priority <n>', 'queue priority (default 0)')
578
+ .option('--language <lang>', 'batch language')
579
+ .option('--funnel <list>', 'funnel metric names, comma-separated')
580
+ .option('--workflow <id>', 'workflow id')
581
+ .action(batches_1.batchesScheduleCommand);
582
+ batches.command('list').description('list batches').option('--limit <n>', 'page size').option('--json', 'raw JSON')
583
+ .action(batches_1.batchesListCommand);
584
+ batches.command('get <batchId>').description('show one batch').option('--json', 'raw JSON').action(batches_1.batchesGetCommand);
585
+ batches.command('status <batchId>').description('batch progress/distribution').option('--json', 'raw JSON')
586
+ .action(batches_1.batchesStatusCommand);
587
+ batches.command('send-next-try <batchId>').description('dial not-connected numbers (next try)')
588
+ .option('--max-try <n>', "max try count (default: batch's current tries)")
589
+ .action(batches_1.batchesSendNextTryCommand);
590
+ batches.command('send-untriggered <batchId>').description('dial never-triggered numbers')
591
+ .action(batches_1.batchesSendUntriggeredCommand);
592
+ batches.command('download <batchId>').description('save receipts/triggered/untriggered/not-connected rows to a file')
593
+ .option('--type <type>', 'receipts | triggered | untriggered | not-connected', 'receipts')
594
+ .option('--file <path>', 'output file (default: batch-<id>-<type>.json)')
595
+ .action(batches_1.batchesDownloadCommand);
596
+ // --- simulations -----------------------------------------------------------
597
+ const sim = program.command('sim').description('agent simulations & test cases');
598
+ sim.command('tests <agentId>').description('list test cases').option('--json', 'raw JSON').action(simulations_1.simTestsListCommand);
599
+ sim.command('create <agentId>').description('create a test case')
600
+ .option('--name <name>', 'test case name')
601
+ .option('--prompt <text>', 'simulated user prompt')
602
+ .option('--criteria <text>', 'success criteria')
603
+ .option('--model <model>', 'test LLM model')
604
+ .action(simulations_1.simCreateCommand);
605
+ sim.command('delete <testCaseId>').description('delete a test case').option('--yes', 'skip confirmation')
606
+ .action(simulations_1.simDeleteCommand);
607
+ sim.command('run <agentId>').description('run a batch simulation (async)')
608
+ .option('--tests <ids>', 'comma-separated test case ids')
609
+ .option('--versions <ids>', 'comma-separated version ids')
610
+ .option('--name <name>', 'run name')
611
+ .action(simulations_1.simRunCommand);
612
+ sim.command('runs <agentId>').description('list simulation runs').option('--json', 'raw JSON').action(simulations_1.simRunsListCommand);
613
+ sim.command('results <batchId>').description('results of a simulation run').option('--json', 'raw JSON')
614
+ .action(simulations_1.simResultsCommand);
615
+ // --- audit strategies ------------------------------------------------------
616
+ const audit = program.command('audit-strategies').alias('audit').description('AI Auditor audit strategies');
617
+ audit.command('list <agentId>').description('list audit strategies for an agent').option('--json', 'raw JSON')
618
+ .action(auditStrategies_1.auditListCommand);
619
+ audit.command('get <agentId> <strategyId>').description('show one audit strategy').option('--json', 'raw JSON')
620
+ .action(auditStrategies_1.auditGetCommand);
621
+ audit.command('filter-options <agentId>')
622
+ .description('drop-off reasons, postcall filters, and hangup reasons available for the agent')
623
+ .option('--json', 'raw JSON')
624
+ .action(auditStrategies_1.auditFilterOptionsCommand);
625
+ audit.command('models').description('list allowed audit models').option('--json', 'raw JSON').action(auditStrategies_1.auditModelsCommand);
626
+ audit.command('create <agentId>').description('create an audit strategy')
627
+ .option('--name <name>', 'strategy name')
628
+ .option('--strategy <text>', 'free-text audit instructions for the LLM auditor (required)')
629
+ .option('--active', 'activate immediately (default: inactive)')
630
+ .option('--disable', 'keep inactive (this is already the default on create)')
631
+ .option('--model <provider/name>', 'audit model, from `cku audit-strategies models` (default callkaro/krishna-2.5)')
632
+ .option('--pct-per-hour <n>', 'percentage of eligible calls to audit each hour (default 100)')
633
+ .option('--max-per-hour <n>', 'max calls to audit per hour, 0 = no limit (default 0)')
634
+ .option('--retranscribe', 'retranscribe audio before auditing')
635
+ .option('--set <json>', 'JSON (or @file.json) for audit_filters/postcall_filters/extra_variables/llms')
636
+ .action(auditStrategies_1.auditCreateCommand);
637
+ audit.command('update <agentId> <strategyId>').description('update an audit strategy (only named fields change)')
638
+ .option('--name <name>', 'strategy name')
639
+ .option('--strategy <text>', 'free-text audit instructions for the LLM auditor')
640
+ .option('--active', 'activate the strategy')
641
+ .option('--disable', 'deactivate the strategy')
642
+ .option('--model <provider/name>', 'audit model, from `cku audit-strategies models`')
643
+ .option('--pct-per-hour <n>', 'percentage of eligible calls to audit each hour')
644
+ .option('--max-per-hour <n>', 'max calls to audit per hour, 0 = no limit')
645
+ .option('--retranscribe', 'retranscribe audio before auditing')
646
+ .option('--set <json>', 'JSON (or @file.json) for audit_filters/postcall_filters/extra_variables/llms — merged onto the existing strategy')
647
+ .action(auditStrategies_1.auditUpdateCommand);
648
+ program.addHelpText('after', `
649
+ ${chalk_1.default.bold('Examples:')}
650
+ ${chalk_1.default.cyan(`${name} register`)} sign up and log in
651
+ ${chalk_1.default.cyan(`${name} login`)} log into an existing account
652
+ ${chalk_1.default.cyan(`${name} accounts`)} list saved logins (* = active)
653
+ ${chalk_1.default.cyan(`${name} accounts use <email>`)} switch account
654
+ ${chalk_1.default.cyan(`${name} whoami`)} see who you're logged in as
655
+ ${chalk_1.default.cyan(`${name} config`)} show settings and session status
656
+ ${chalk_1.default.cyan(`${name} agents list`)} list your agents in a table
657
+ ${chalk_1.default.cyan(`${name} agents list --json`)} output raw JSON
658
+ ${chalk_1.default.cyan(`${name} agents update <agentId> --set '{"name":"New"}' --commit "rename"`)}
659
+ ${chalk_1.default.cyan(`${name} calls make --agent <id> --to <number> --test`)} place a test call
660
+ `);
661
+ // Bare invocation → show help instead of an error.
662
+ if (process.argv.length <= 2) {
663
+ program.outputHelp();
664
+ process.exit(0);
665
+ }
666
+ program.parseAsync(rewriteGetVersions(process.argv));
667
+ /**
668
+ * Rewrite `agents get versions <agentId>` → `agents versions <agentId>`.
669
+ *
670
+ * `get` takes a required `<agentId>`, so commander cannot also give it a
671
+ * `versions` subcommand. Left alone it binds "versions" to `<agentId>`, sends
672
+ * that to the backend, and the ObjectId guard reports "Invalid id: not a valid
673
+ * id" — an error about a word the user never meant as an id. Since `agents get
674
+ * versions` reads naturally and has no other valid meaning, accept it.
675
+ *
676
+ * Only the exact three-token prefix is rewritten; `agents get <id> --versions x`
677
+ * is a different, still-valid command and must pass through untouched.
678
+ */
679
+ function rewriteGetVersions(argv) {
680
+ const i = argv.indexOf('agents');
681
+ if (i === -1 || argv[i + 1] !== 'get' || argv[i + 2] !== 'versions')
682
+ return argv;
683
+ return [...argv.slice(0, i + 1), ...argv.slice(i + 2)]; // drop the 'get'
684
+ }