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
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ /**
3
+ * Runs a shell-style text query over a call's raw log without ever printing
4
+ * the blob URL. The URL (from api.getCall's logfile_url) is only used here,
5
+ * internally, to download the log text once per command invocation.
6
+ *
7
+ * just-bash is ESM-only; this package builds to CommonJS, so it's loaded
8
+ * with a dynamic import().
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.LogNotFoundError = void 0;
45
+ exports.unavailableReason = unavailableReason;
46
+ exports.getLogText = getLogText;
47
+ exports.runLogQuery = runLogQuery;
48
+ const MAX_LOG_BYTES = 8 * 1024 * 1024;
49
+ const FETCH_TIMEOUT_MS = 15_000;
50
+ const MAX_RESULT_CHARS = 20_000; // CLI output isn't token-budgeted like an LLM tool result
51
+ const MAX_LINE_CHARS = 1_000;
52
+ const LOG_UNAVAILABLE_MESSAGE = 'No log available for this call. If the call was made recently, wait a bit and try again -- ' +
53
+ 'otherwise the log has likely expired (logs are kept for 7 days).';
54
+ class LogNotFoundError extends Error {
55
+ constructor() {
56
+ super('Log file not found at storage.');
57
+ this.name = 'LogNotFoundError';
58
+ }
59
+ }
60
+ exports.LogNotFoundError = LogNotFoundError;
61
+ function unavailableReason(_call) {
62
+ return LOG_UNAVAILABLE_MESSAGE;
63
+ }
64
+ function isTrustedLogHost(hostname) {
65
+ return hostname.toLowerCase().endsWith('.blob.core.windows.net');
66
+ }
67
+ async function downloadLogText(logfileUrl) {
68
+ let url;
69
+ try {
70
+ url = new URL(logfileUrl);
71
+ }
72
+ catch {
73
+ throw new Error('Stored log URL is malformed.');
74
+ }
75
+ // Host allowlist + no redirects: a blob URL from the API must not be able to
76
+ // send us somewhere else, and a redirect would bypass the host check.
77
+ if (url.protocol !== 'https:' || !isTrustedLogHost(url.hostname)) {
78
+ throw new Error('Stored log URL host is not allowed.');
79
+ }
80
+ const response = await fetch(url, {
81
+ redirect: 'error',
82
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
83
+ });
84
+ if (response.status === 404)
85
+ throw new LogNotFoundError();
86
+ if (!response.ok || !response.body) {
87
+ throw new Error(`Log download failed with HTTP ${response.status}.`);
88
+ }
89
+ const decoder = new TextDecoder('utf-8');
90
+ const reader = response.body.getReader();
91
+ const chunks = [];
92
+ let bytes = 0;
93
+ try {
94
+ while (true) {
95
+ const { done, value } = await reader.read();
96
+ if (done)
97
+ break;
98
+ if (bytes + value.byteLength > MAX_LOG_BYTES) {
99
+ chunks.push(decoder.decode(value.subarray(0, MAX_LOG_BYTES - bytes), { stream: true }));
100
+ await reader.cancel();
101
+ break;
102
+ }
103
+ bytes += value.byteLength;
104
+ chunks.push(decoder.decode(value, { stream: true }));
105
+ }
106
+ }
107
+ finally {
108
+ reader.releaseLock();
109
+ }
110
+ chunks.push(decoder.decode());
111
+ return chunks.join('');
112
+ }
113
+ /** Fetches a call's raw log text. Throws LogNotFoundError when there's no log yet. */
114
+ async function getLogText(call) {
115
+ if (!call.logfile_url)
116
+ throw new LogNotFoundError();
117
+ return downloadLogText(call.logfile_url);
118
+ }
119
+ /**
120
+ * Runs a shell-style query (grep/awk/sed/cut/sort/uniq/head/tail/wc, piped)
121
+ * over the log text in a sandboxed, in-process shell -- no real process is
122
+ * ever spawned, so this is safe on any query typed by a user or an LLM.
123
+ */
124
+ async function runLogQuery(text, query) {
125
+ const { Bash } = await Promise.resolve().then(() => __importStar(require('just-bash')));
126
+ const bash = new Bash({ files: { '/call.log': text } });
127
+ const { stdout, stderr, exitCode } = await bash.exec(query);
128
+ if (exitCode !== 0) {
129
+ throw new Error(stderr || `Query exited with code ${exitCode}.`);
130
+ }
131
+ const cappedLines = stdout
132
+ .split('\n')
133
+ .map((line) => (line.length > MAX_LINE_CHARS ? `${line.slice(0, MAX_LINE_CHARS)} …[line truncated]` : line))
134
+ .join('\n');
135
+ const truncated = cappedLines.length > MAX_RESULT_CHARS;
136
+ return {
137
+ result: truncated ? cappedLines.slice(0, MAX_RESULT_CHARS) : cappedLines,
138
+ truncated,
139
+ };
140
+ }
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readJsonFile = readJsonFile;
4
+ exports.stripBom = stripBom;
5
+ exports.parseLooseJson = parseLooseJson;
6
+ const fs_1 = require("fs");
7
+ /**
8
+ * Read a UTF-8 text file, dropping a leading byte-order mark.
9
+ *
10
+ * Windows editors and `Out-File -Encoding utf8` (PowerShell 5.1) write a BOM by
11
+ * default, and `JSON.parse` rejects it as an unexpected token — so a JSON file
12
+ * that looks perfectly fine fails to load. Node's 'utf8' decoding keeps the BOM
13
+ * as U+FEFF, so strip it here, once, for every file we parse.
14
+ */
15
+ function readJsonFile(path) {
16
+ return stripBom((0, fs_1.readFileSync)(path, 'utf8'));
17
+ }
18
+ /** Drop a leading U+FEFF, if present. Also applied to stdin, which can carry one. */
19
+ function stripBom(text) {
20
+ return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
21
+ }
22
+ /** Bare tokens that stay unquoted, because JSON gives them a non-string meaning. */
23
+ const LITERAL = /^(true|false|null)$/;
24
+ const NUMBER = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?$/;
25
+ /** Characters that end a bare value / a bare key at the current nesting level. */
26
+ const VALUE_STOP = ',}]';
27
+ const KEY_STOP = ':,}';
28
+ /**
29
+ * Parse `raw` as JSON, repairing shell-stripped quotes if the strict parse fails.
30
+ *
31
+ * PowerShell strips the double quotes out of a single-quoted argument before
32
+ * handing it to a native program, so
33
+ *
34
+ * ck agents update <id> --set '{"name":"mini"}'
35
+ *
36
+ * arrives in `process.argv` as `{name:mini}` — no longer JSON. `parseLooseJson`
37
+ * puts the quotes back instead: it tries strict `JSON.parse` first and only on
38
+ * failure runs a repair pass that re-quotes bare keys and bare scalar values
39
+ * (and accepts 'single-quoted' strings, which shells also mangle).
40
+ *
41
+ * Valid JSON always takes the strict path, so nothing well-formed is reinterpreted.
42
+ *
43
+ * Use this ONLY for values that came from argv. File and stdin contents are not
44
+ * touched by the shell, so those stay on strict `JSON.parse` — a genuine typo in
45
+ * a file should be reported, not guessed at.
46
+ */
47
+ function parseLooseJson(raw) {
48
+ const text = raw.trim();
49
+ try {
50
+ return JSON.parse(text);
51
+ }
52
+ catch (strictErr) {
53
+ // Only repair something already shaped like an object/array. A bare word is
54
+ // not mangled JSON, it is just wrong input, and should say so.
55
+ if (text[0] !== '{' && text[0] !== '[')
56
+ throw strictErr;
57
+ let repaired;
58
+ try {
59
+ repaired = requote(text);
60
+ }
61
+ catch {
62
+ throw strictErr;
63
+ }
64
+ try {
65
+ return JSON.parse(repaired);
66
+ }
67
+ catch {
68
+ throw strictErr;
69
+ }
70
+ }
71
+ }
72
+ /**
73
+ * Rewrite quote-stripped JSON-ish text into strict JSON.
74
+ *
75
+ * A scanner rather than a regex, so nesting, and values containing `:` (URLs) or
76
+ * spaces, survive: a bare token runs to the next structural character at its own
77
+ * level, then gets quoted unless it reads as a number or a JSON literal.
78
+ */
79
+ function requote(text) {
80
+ let i = 0;
81
+ const skipWs = () => {
82
+ while (i < text.length && /\s/.test(text[i]))
83
+ i++;
84
+ };
85
+ const fail = (msg) => {
86
+ throw new Error(`${msg} at position ${i}`);
87
+ };
88
+ function value() {
89
+ skipWs();
90
+ const c = text[i];
91
+ if (c === undefined)
92
+ return fail('unexpected end of input');
93
+ if (c === '{')
94
+ return object();
95
+ if (c === '[')
96
+ return array();
97
+ if (c === '"' || c === "'")
98
+ return str(c);
99
+ return bareValue();
100
+ }
101
+ function object() {
102
+ i++; // consume '{'
103
+ skipWs();
104
+ if (text[i] === '}') {
105
+ i++;
106
+ return '{}';
107
+ }
108
+ const parts = [];
109
+ for (;;) {
110
+ skipWs();
111
+ const c = text[i];
112
+ const key = c === '"' || c === "'" ? str(c) : bareKey();
113
+ skipWs();
114
+ if (text[i] !== ':')
115
+ return fail('expected ":" after object key');
116
+ i++;
117
+ parts.push(`${key}:${value()}`);
118
+ skipWs();
119
+ if (text[i] === ',') {
120
+ i++;
121
+ continue;
122
+ }
123
+ if (text[i] === '}') {
124
+ i++;
125
+ return `{${parts.join(',')}}`;
126
+ }
127
+ return fail('expected "," or "}"');
128
+ }
129
+ }
130
+ function array() {
131
+ i++; // consume '['
132
+ skipWs();
133
+ if (text[i] === ']') {
134
+ i++;
135
+ return '[]';
136
+ }
137
+ const parts = [];
138
+ for (;;) {
139
+ parts.push(value());
140
+ skipWs();
141
+ if (text[i] === ',') {
142
+ i++;
143
+ continue;
144
+ }
145
+ if (text[i] === ']') {
146
+ i++;
147
+ return `[${parts.join(',')}]`;
148
+ }
149
+ return fail('expected "," or "]"');
150
+ }
151
+ }
152
+ /** Read a quoted string and re-emit it as a valid double-quoted JSON string. */
153
+ function str(quote) {
154
+ i++; // consume the opening quote
155
+ let body = '';
156
+ while (i < text.length && text[i] !== quote) {
157
+ if (text[i] === '\\' && i + 1 < text.length) {
158
+ body += text[i] + text[i + 1];
159
+ i += 2;
160
+ continue;
161
+ }
162
+ body += text[i++];
163
+ }
164
+ if (text[i] !== quote)
165
+ return fail('unterminated string');
166
+ i++; // consume the closing quote
167
+ // Double-quoted bodies already carry JSON escapes, so pass them through.
168
+ // Single-quoted ones may contain \' (not a JSON escape) and unescaped " —
169
+ // unescape the former and let JSON.stringify handle the rest.
170
+ return quote === '"' ? `"${body}"` : JSON.stringify(body.replace(/\\'/g, "'"));
171
+ }
172
+ function bareValue() {
173
+ const token = readUntil(VALUE_STOP);
174
+ if (token === '')
175
+ return fail('empty value');
176
+ if (LITERAL.test(token) || NUMBER.test(token))
177
+ return token;
178
+ return JSON.stringify(token);
179
+ }
180
+ /** Keys are always strings in JSON — quote even a numeric-looking one. */
181
+ function bareKey() {
182
+ const token = readUntil(KEY_STOP);
183
+ if (token === '')
184
+ return fail('empty object key');
185
+ return JSON.stringify(token);
186
+ }
187
+ function readUntil(stops) {
188
+ const start = i;
189
+ while (i < text.length && !stops.includes(text[i]))
190
+ i++;
191
+ return text.slice(start, i).trim();
192
+ }
193
+ const out = value();
194
+ skipWs();
195
+ if (i < text.length)
196
+ fail('unexpected trailing input');
197
+ return out;
198
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireApi = requireApi;
4
+ const config_1 = require("./config");
5
+ const ui_1 = require("./ui");
6
+ /**
7
+ * Get an authenticated API client, or exit if not logged in. Shared by all
8
+ * commands — the single auth gate.
9
+ */
10
+ function requireApi() {
11
+ const { api, cfg } = (0, config_1.getClient)();
12
+ if (!cfg.token && !process.env.CK_TOKEN) {
13
+ (0, ui_1.fail)('Not logged in. Run `cku login`.');
14
+ process.exit(1);
15
+ }
16
+ return api;
17
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiError = void 0;
4
+ /** Thrown for any non-2xx API response; carries the HTTP status and field details. */
5
+ class ApiError extends Error {
6
+ status;
7
+ details;
8
+ constructor(status, message, details = []) {
9
+ super(message);
10
+ this.status = status;
11
+ this.details = details;
12
+ this.name = 'ApiError';
13
+ }
14
+ }
15
+ exports.ApiError = ApiError;
package/dist/lib/ui.js ADDED
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.fail = exports.info = exports.success = void 0;
7
+ exports.banner = banner;
8
+ exports.keyValue = keyValue;
9
+ exports.table = table;
10
+ exports.handleError = handleError;
11
+ exports.intFlag = intFlag;
12
+ const chalk_1 = __importDefault(require("chalk"));
13
+ const cli_table3_1 = __importDefault(require("cli-table3"));
14
+ const types_1 = require("./types");
15
+ const success = (msg) => console.log(chalk_1.default.green('✔') + ' ' + msg);
16
+ exports.success = success;
17
+ const info = (msg) => console.log(msg);
18
+ exports.info = info;
19
+ const fail = (msg) => console.error(chalk_1.default.red('✖') + ' ' + msg);
20
+ exports.fail = fail;
21
+ /** Styled wordmark shown at the top of help (ANSI Shadow "CKU"). */
22
+ function banner(version) {
23
+ const art = [
24
+ ' ██████╗██╗ ██╗██╗ ██╗',
25
+ '██╔════╝██║ ██╔╝██║ ██║',
26
+ '██║ █████╔╝ ██║ ██║',
27
+ '██║ ██╔═██╗ ██║ ██║',
28
+ '╚██████╗██║ ██╗╚██████╔╝',
29
+ ' ╚═════╝╚═╝ ╚═╝ ╚═════╝ ',
30
+ ].join('\n');
31
+ return ('\n' +
32
+ chalk_1.default.cyan.bold(art) +
33
+ chalk_1.default.magenta(' (unofficial fork)') +
34
+ '\n' +
35
+ chalk_1.default.dim(' Voice AI agents, straight from your terminal') +
36
+ chalk_1.default.dim(` · v${version}`) +
37
+ '\n');
38
+ }
39
+ /** Print an aligned key/value block (used for the account profile). */
40
+ function keyValue(obj) {
41
+ const entries = Object.entries(obj).filter(([, v]) => v !== undefined && v !== null);
42
+ if (entries.length === 0)
43
+ return;
44
+ const width = Math.max(...entries.map(([k]) => k.length));
45
+ for (const [k, v] of entries) {
46
+ console.log(`${chalk_1.default.dim(k.padEnd(width))} ${v}`);
47
+ }
48
+ }
49
+ /** Render rows as a bordered table with a highlighted header. */
50
+ function table(headers, rows) {
51
+ const t = new cli_table3_1.default({ head: headers.map((h) => chalk_1.default.cyan(h)) });
52
+ for (const row of rows) {
53
+ t.push(row.map((cell) => (cell === undefined || cell === null ? '' : String(cell))));
54
+ }
55
+ console.log(t.toString());
56
+ }
57
+ /**
58
+ * Turn any thrown error into a clean one-line message + non-zero exit.
59
+ *
60
+ * Sets `exitCode` instead of `process.exit()`: on Windows, exiting while a fetch
61
+ * socket is still closing trips a libuv assertion (UV_HANDLE_CLOSING) that aborts
62
+ * with code 127 and C-level noise. Letting Node drain and exit keeps a clean
63
+ * exit 1. Callers return right after, so control doesn't continue.
64
+ */
65
+ function handleError(err) {
66
+ (0, exports.fail)(err instanceof Error ? err.message : 'Unexpected error');
67
+ if (err instanceof types_1.ApiError) {
68
+ for (const detail of err.details) {
69
+ const path = detail.path ? `${detail.path}: ` : '';
70
+ const code = detail.code ? chalk_1.default.dim(` [${detail.code}]`) : '';
71
+ console.error(` ${chalk_1.default.cyan(path)}${detail.message}${code}`);
72
+ }
73
+ }
74
+ process.exitCode = 1;
75
+ }
76
+ /**
77
+ * Parse a positive-integer CLI flag, or return `fallback` when absent.
78
+ *
79
+ * `Number('lots')` is NaN, and NaN serialises to `null` in a query string or a
80
+ * JSON body — so the backend silently receives a null page size instead of the
81
+ * command reporting a typo. The flag name is passed in so the error names it.
82
+ */
83
+ function intFlag(raw, flag, fallback) {
84
+ if (raw === undefined || raw === null || raw === '')
85
+ return fallback;
86
+ const n = Number(raw);
87
+ if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
88
+ throw new Error(`${flag} must be a positive whole number (got "${raw}").`);
89
+ }
90
+ return n;
91
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ /**
3
+ * Every command that targets a version uses the same flag: `--versions`.
4
+ * (Plain `--version` is unusable — it collides with the global `-v, --version`.)
5
+ *
6
+ * Some commands take a list (export, ab, filters); others target one version.
7
+ * These helpers keep that single flag forgiving in both cases.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.versionList = versionList;
11
+ exports.firstVersion = firstVersion;
12
+ /** Split a comma-separated `--versions` value into ids. */
13
+ function versionList(spec) {
14
+ return (spec || '').split(',').map((s) => s.trim()).filter(Boolean);
15
+ }
16
+ /** First id from `--versions` — for commands that act on a single version. */
17
+ function firstVersion(spec) {
18
+ return versionList(spec)[0];
19
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "callkaro-cli-unofficial",
3
+ "version": "0.1.0",
4
+ "license": "Apache-2.0",
5
+ "description": "Unofficial fork of the CallKaro CLI \u2014 manage your voice AI agents from the terminal",
6
+ "bin": {
7
+ "cku": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "npm run clean && tsc -p tsconfig.json && node -e \"require('fs').copyFileSync('src/endpoints.json','dist/endpoints.json')\" && node -e \"try{require('fs').chmodSync('dist/index.js',0o755)}catch(e){}\"",
14
+ "dev": "npm run build && node dist/index.js",
15
+ "test": "npm run build && node --test tests/*.test.js",
16
+ "test:only": "node --test tests/*.test.js",
17
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
18
+ "catalog:sync": "node scripts/sync-catalog.js",
19
+ "catalog:check": "node scripts/check-catalog-drift.js",
20
+ "catalog:check:live": "node scripts/check-catalog-drift.js --live"
21
+ },
22
+ "dependencies": {
23
+ "chalk": "^4.1.2",
24
+ "cli-table3": "^0.6.5",
25
+ "commander": "^12.0.0",
26
+ "just-bash": "^3.4.2",
27
+ "prompts": "^2.4.2"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^20.0.0",
31
+ "@types/prompts": "^2.4.9",
32
+ "typescript": "^5.4.0"
33
+ },
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/TejasLamba2006/callkaro-cli-unofficial.git"
40
+ },
41
+ "homepage": "https://github.com/TejasLamba2006/callkaro-cli-unofficial#readme",
42
+ "bugs": {
43
+ "url": "https://github.com/TejasLamba2006/callkaro-cli-unofficial/issues"
44
+ },
45
+ "keywords": [
46
+ "callkaro",
47
+ "voice-ai",
48
+ "cli",
49
+ "agents",
50
+ "telephony"
51
+ ]
52
+ }