retell-cli 1.1.0 → 1.2.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.
- package/dist/index.js +889 -290
- package/package.json +17 -9
package/dist/index.js
CHANGED
|
@@ -114,11 +114,9 @@ function saveConfig(config) {
|
|
|
114
114
|
}
|
|
115
115
|
const configPath = getConfigPath();
|
|
116
116
|
try {
|
|
117
|
-
(0, import_fs.writeFileSync)(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
{ encoding: "utf-8" }
|
|
121
|
-
);
|
|
117
|
+
(0, import_fs.writeFileSync)(configPath, JSON.stringify(validatedConfig, null, 2), {
|
|
118
|
+
encoding: "utf-8"
|
|
119
|
+
});
|
|
122
120
|
(0, import_fs.chmodSync)(configPath, CONFIG_FILE_PERMISSIONS);
|
|
123
121
|
} catch (error) {
|
|
124
122
|
throw new ConfigError(
|
|
@@ -240,16 +238,28 @@ function handleSdkError(error) {
|
|
|
240
238
|
outputError("Rate limit exceeded. Please try again later.", "RATE_LIMIT");
|
|
241
239
|
}
|
|
242
240
|
if (error instanceof import_retell_sdk.default.PermissionDeniedError) {
|
|
243
|
-
outputError(
|
|
241
|
+
outputError(
|
|
242
|
+
"Permission denied. Check your API key permissions.",
|
|
243
|
+
"PERMISSION_DENIED"
|
|
244
|
+
);
|
|
244
245
|
}
|
|
245
246
|
if (error instanceof import_retell_sdk.default.InternalServerError) {
|
|
246
|
-
outputError(
|
|
247
|
+
outputError(
|
|
248
|
+
"Retell API server error. Please try again later.",
|
|
249
|
+
"SERVER_ERROR"
|
|
250
|
+
);
|
|
247
251
|
}
|
|
248
252
|
if (error instanceof import_retell_sdk.default.APIConnectionError) {
|
|
249
|
-
outputError(
|
|
253
|
+
outputError(
|
|
254
|
+
"Failed to connect to Retell API. Check your network connection.",
|
|
255
|
+
"CONNECTION_ERROR"
|
|
256
|
+
);
|
|
250
257
|
}
|
|
251
258
|
if (error instanceof import_retell_sdk.default.APIConnectionTimeoutError) {
|
|
252
|
-
outputError(
|
|
259
|
+
outputError(
|
|
260
|
+
"Request to Retell API timed out. Please try again.",
|
|
261
|
+
"TIMEOUT_ERROR"
|
|
262
|
+
);
|
|
253
263
|
}
|
|
254
264
|
if (error instanceof import_retell_sdk.default.APIError) {
|
|
255
265
|
const message = error.message || "An API error occurred";
|
|
@@ -304,7 +314,9 @@ async function loginCommand() {
|
|
|
304
314
|
const rl = readline.createInterface({ input: import_process.stdin, output: import_process.stdout });
|
|
305
315
|
try {
|
|
306
316
|
if (configFileExists()) {
|
|
307
|
-
const overwrite = await rl.question(
|
|
317
|
+
const overwrite = await rl.question(
|
|
318
|
+
"Config already exists. Overwrite? (y/n): "
|
|
319
|
+
);
|
|
308
320
|
if (overwrite.toLowerCase() !== "y") {
|
|
309
321
|
rl.close();
|
|
310
322
|
outputJson({ message: "Login cancelled" });
|
|
@@ -327,10 +339,7 @@ async function loginCommand() {
|
|
|
327
339
|
outputJson({
|
|
328
340
|
message: "Successfully authenticated!",
|
|
329
341
|
configPath: "./.retellrc.json",
|
|
330
|
-
nextSteps: [
|
|
331
|
-
"Try: retell agents list",
|
|
332
|
-
"Try: retell transcripts list"
|
|
333
|
-
]
|
|
342
|
+
nextSteps: ["Try: retell agents list", "Try: retell transcripts list"]
|
|
334
343
|
});
|
|
335
344
|
} catch (error) {
|
|
336
345
|
rl.close();
|
|
@@ -357,7 +366,9 @@ function getRetellClient() {
|
|
|
357
366
|
if (error instanceof ConfigError) {
|
|
358
367
|
throw error;
|
|
359
368
|
}
|
|
360
|
-
throw new Error(
|
|
369
|
+
throw new Error(
|
|
370
|
+
`Failed to initialize Retell client: ${error.message}`
|
|
371
|
+
);
|
|
361
372
|
}
|
|
362
373
|
clientInstance = new import_retell_sdk3.default({
|
|
363
374
|
apiKey,
|
|
@@ -375,7 +386,10 @@ async function listTranscriptsCommand(options) {
|
|
|
375
386
|
const calls = await client.call.list({
|
|
376
387
|
limit: options.limit || 50
|
|
377
388
|
});
|
|
378
|
-
const output = options.fields ? filterFields(
|
|
389
|
+
const output = options.fields ? filterFields(
|
|
390
|
+
calls,
|
|
391
|
+
options.fields.split(",").map((f) => f.trim())
|
|
392
|
+
) : calls;
|
|
379
393
|
outputJson(output);
|
|
380
394
|
} catch (error) {
|
|
381
395
|
handleSdkError(error);
|
|
@@ -387,7 +401,10 @@ async function getTranscriptCommand(callId, options = {}) {
|
|
|
387
401
|
try {
|
|
388
402
|
const client = getRetellClient();
|
|
389
403
|
const call = await client.call.retrieve(callId);
|
|
390
|
-
const output = options.fields ? filterFields(
|
|
404
|
+
const output = options.fields ? filterFields(
|
|
405
|
+
call,
|
|
406
|
+
options.fields.split(",").map((f) => f.trim())
|
|
407
|
+
) : call;
|
|
391
408
|
outputJson(output);
|
|
392
409
|
} catch (error) {
|
|
393
410
|
handleSdkError(error);
|
|
@@ -413,8 +430,7 @@ function extractTranscriptTurns(transcriptObject) {
|
|
|
413
430
|
}));
|
|
414
431
|
}
|
|
415
432
|
function formatTimestamp(seconds) {
|
|
416
|
-
if (!seconds || seconds < 0)
|
|
417
|
-
return "N/A";
|
|
433
|
+
if (!seconds || seconds < 0) return "N/A";
|
|
418
434
|
const totalSeconds = Math.floor(seconds);
|
|
419
435
|
const mins = Math.floor(totalSeconds / 60);
|
|
420
436
|
const secs = totalSeconds % 60;
|
|
@@ -449,12 +465,10 @@ function detectLongSilences(call, config) {
|
|
|
449
465
|
const currTurn = transcript[i];
|
|
450
466
|
const prevWords = prevTurn.words || [];
|
|
451
467
|
const currWords = currTurn.words || [];
|
|
452
|
-
if (prevWords.length === 0 || currWords.length === 0)
|
|
453
|
-
continue;
|
|
468
|
+
if (prevWords.length === 0 || currWords.length === 0) continue;
|
|
454
469
|
const prevEnd = prevWords[prevWords.length - 1]?.end;
|
|
455
470
|
const currStart = currWords[0]?.start;
|
|
456
|
-
if (prevEnd === void 0 || currStart === void 0)
|
|
457
|
-
continue;
|
|
471
|
+
if (prevEnd === void 0 || currStart === void 0) continue;
|
|
458
472
|
const gapMs = (currStart - prevEnd) * 1e3;
|
|
459
473
|
if (gapMs > config.silenceThreshold) {
|
|
460
474
|
hotspots.push({
|
|
@@ -492,10 +506,8 @@ function detectAllHotspots(call, config) {
|
|
|
492
506
|
...detectSentimentIssues(call)
|
|
493
507
|
];
|
|
494
508
|
return hotspots.sort((a, b) => {
|
|
495
|
-
if (a.turn_index === -1)
|
|
496
|
-
|
|
497
|
-
if (b.turn_index === -1)
|
|
498
|
-
return 1;
|
|
509
|
+
if (a.turn_index === -1) return -1;
|
|
510
|
+
if (b.turn_index === -1) return 1;
|
|
499
511
|
return a.turn_index - b.turn_index;
|
|
500
512
|
});
|
|
501
513
|
}
|
|
@@ -510,7 +522,10 @@ async function analyzeTranscriptCommand(callId, options = {}) {
|
|
|
510
522
|
const client = getRetellClient();
|
|
511
523
|
const call = await client.call.retrieve(callId);
|
|
512
524
|
if (options.raw) {
|
|
513
|
-
const output2 = options.fields ? filterFields(
|
|
525
|
+
const output2 = options.fields ? filterFields(
|
|
526
|
+
call,
|
|
527
|
+
options.fields.split(",").map((f) => f.trim())
|
|
528
|
+
) : call;
|
|
514
529
|
outputJson(output2);
|
|
515
530
|
return;
|
|
516
531
|
}
|
|
@@ -524,7 +539,10 @@ async function analyzeTranscriptCommand(callId, options = {}) {
|
|
|
524
539
|
call_id: callId,
|
|
525
540
|
hotspots
|
|
526
541
|
};
|
|
527
|
-
const output2 = options.fields ? filterFields(
|
|
542
|
+
const output2 = options.fields ? filterFields(
|
|
543
|
+
result,
|
|
544
|
+
options.fields.split(",").map((f) => f.trim())
|
|
545
|
+
) : result;
|
|
528
546
|
outputJson(output2);
|
|
529
547
|
return;
|
|
530
548
|
}
|
|
@@ -561,7 +579,10 @@ async function analyzeTranscriptCommand(callId, options = {}) {
|
|
|
561
579
|
breakdown: call.call_cost?.product_costs || []
|
|
562
580
|
}
|
|
563
581
|
};
|
|
564
|
-
const output = options.fields ? filterFields(
|
|
582
|
+
const output = options.fields ? filterFields(
|
|
583
|
+
analysis,
|
|
584
|
+
options.fields.split(",").map((f) => f.trim())
|
|
585
|
+
) : analysis;
|
|
565
586
|
outputJson(output);
|
|
566
587
|
} catch (error) {
|
|
567
588
|
handleSdkError(error);
|
|
@@ -594,11 +615,15 @@ function parseDate(dateStr) {
|
|
|
594
615
|
const dateOnlyPattern = /^\d{4}-\d{2}-\d{2}$/;
|
|
595
616
|
const iso8601Pattern = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
596
617
|
if (!iso8601Pattern.test(dateStr)) {
|
|
597
|
-
throw new ValidationError(
|
|
618
|
+
throw new ValidationError(
|
|
619
|
+
`Invalid date format: "${dateStr}". Use YYYY-MM-DD or ISO 8601 format.`
|
|
620
|
+
);
|
|
598
621
|
}
|
|
599
622
|
const date = new Date(dateStr);
|
|
600
623
|
if (isNaN(date.getTime())) {
|
|
601
|
-
throw new ValidationError(
|
|
624
|
+
throw new ValidationError(
|
|
625
|
+
`Invalid date format: "${dateStr}". Use YYYY-MM-DD or ISO 8601 format.`
|
|
626
|
+
);
|
|
602
627
|
}
|
|
603
628
|
return { date, isDateOnly: dateOnlyPattern.test(dateStr) };
|
|
604
629
|
}
|
|
@@ -629,10 +654,14 @@ function validateSearchOptions(options) {
|
|
|
629
654
|
}
|
|
630
655
|
}
|
|
631
656
|
if (options.limit !== void 0 && (options.limit < 1 || !Number.isInteger(options.limit))) {
|
|
632
|
-
throw new ValidationError(
|
|
657
|
+
throw new ValidationError(
|
|
658
|
+
`Limit must be a positive integer (got: "${options.limit}")`
|
|
659
|
+
);
|
|
633
660
|
}
|
|
634
661
|
if (options.limit !== void 0 && options.limit > MAX_LIMIT) {
|
|
635
|
-
throw new ValidationError(
|
|
662
|
+
throw new ValidationError(
|
|
663
|
+
`Limit cannot exceed ${MAX_LIMIT} (got: "${options.limit}")`
|
|
664
|
+
);
|
|
636
665
|
}
|
|
637
666
|
return parsedDates;
|
|
638
667
|
}
|
|
@@ -665,7 +694,10 @@ async function searchTranscripts(options, parsedDates) {
|
|
|
665
694
|
const response = await client.call.list(apiParams);
|
|
666
695
|
const validation = CallListSchema.safeParse(response);
|
|
667
696
|
if (!validation.success) {
|
|
668
|
-
console.warn(
|
|
697
|
+
console.warn(
|
|
698
|
+
"Warning: API response validation failed:",
|
|
699
|
+
validation.error.message
|
|
700
|
+
);
|
|
669
701
|
throw new Error(`Invalid API response format: ${validation.error.message}`);
|
|
670
702
|
}
|
|
671
703
|
const results = validation.data;
|
|
@@ -694,7 +726,10 @@ async function searchTranscriptsCommand(options = {}) {
|
|
|
694
726
|
const searchResult = await searchTranscripts(options, parsedDates);
|
|
695
727
|
const output = options.fields ? {
|
|
696
728
|
results: searchResult.results.map(
|
|
697
|
-
(r) => filterFields(
|
|
729
|
+
(r) => filterFields(
|
|
730
|
+
r,
|
|
731
|
+
options.fields.split(",").map((f) => f.trim())
|
|
732
|
+
)
|
|
698
733
|
),
|
|
699
734
|
total_count: searchResult.total_count,
|
|
700
735
|
filters_applied: searchResult.filters_applied
|
|
@@ -740,7 +775,10 @@ async function listAgentsCommand(options = {}) {
|
|
|
740
775
|
response_engine_id
|
|
741
776
|
};
|
|
742
777
|
});
|
|
743
|
-
const output = options.fields ? filterFields(
|
|
778
|
+
const output = options.fields ? filterFields(
|
|
779
|
+
formatted,
|
|
780
|
+
options.fields.split(",").map((f) => f.trim())
|
|
781
|
+
) : formatted;
|
|
744
782
|
outputJson(output);
|
|
745
783
|
} catch (error) {
|
|
746
784
|
handleSdkError(error);
|
|
@@ -752,7 +790,10 @@ async function agentInfoCommand(agentId, options = {}) {
|
|
|
752
790
|
try {
|
|
753
791
|
const client = getRetellClient();
|
|
754
792
|
const agent = await client.agent.retrieve(agentId);
|
|
755
|
-
const output = options.fields ? filterFields(
|
|
793
|
+
const output = options.fields ? filterFields(
|
|
794
|
+
agent,
|
|
795
|
+
options.fields.split(",").map((f) => f.trim())
|
|
796
|
+
) : agent;
|
|
756
797
|
outputJson(output);
|
|
757
798
|
} catch (error) {
|
|
758
799
|
handleSdkError(error);
|
|
@@ -807,7 +848,9 @@ async function resolvePromptSource(agentId) {
|
|
|
807
848
|
// src/commands/prompts/pull.ts
|
|
808
849
|
function validateAgentId(agentId) {
|
|
809
850
|
if (agentId.includes("..") || agentId.includes("/") || agentId.includes("\\")) {
|
|
810
|
-
throw new Error(
|
|
851
|
+
throw new Error(
|
|
852
|
+
"Invalid agent ID: cannot contain path separators or traversal sequences"
|
|
853
|
+
);
|
|
811
854
|
}
|
|
812
855
|
}
|
|
813
856
|
async function pullPromptsCommand(agentId, options) {
|
|
@@ -824,7 +867,10 @@ async function pullPromptsCommand(agentId, options) {
|
|
|
824
867
|
(0, import_fs2.mkdirSync)(agentDir, { recursive: true });
|
|
825
868
|
} catch (error) {
|
|
826
869
|
if (error.code === "EACCES") {
|
|
827
|
-
outputError(
|
|
870
|
+
outputError(
|
|
871
|
+
`Permission denied creating directory: ${agentDir}`,
|
|
872
|
+
"PERMISSION_DENIED"
|
|
873
|
+
);
|
|
828
874
|
} else if (error.code === "ENOSPC") {
|
|
829
875
|
outputError(`No space left on device: ${agentDir}`, "NO_SPACE");
|
|
830
876
|
} else {
|
|
@@ -840,11 +886,17 @@ async function pullPromptsCommand(agentId, options) {
|
|
|
840
886
|
}
|
|
841
887
|
} catch (error) {
|
|
842
888
|
if (error.code === "EACCES") {
|
|
843
|
-
outputError(
|
|
889
|
+
outputError(
|
|
890
|
+
`Permission denied writing files to: ${agentDir}`,
|
|
891
|
+
"PERMISSION_DENIED"
|
|
892
|
+
);
|
|
844
893
|
} else if (error.code === "ENOSPC") {
|
|
845
894
|
outputError(`No space left on device: ${agentDir}`, "NO_SPACE");
|
|
846
895
|
} else {
|
|
847
|
-
outputError(
|
|
896
|
+
outputError(
|
|
897
|
+
`Failed to write prompt files: ${error.message}`,
|
|
898
|
+
"FS_ERROR"
|
|
899
|
+
);
|
|
848
900
|
}
|
|
849
901
|
return;
|
|
850
902
|
}
|
|
@@ -869,8 +921,14 @@ function saveRetellLlmPrompts(agentDir, promptSource) {
|
|
|
869
921
|
version: prompts2.version,
|
|
870
922
|
pulled_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
871
923
|
};
|
|
872
|
-
(0, import_fs2.writeFileSync)(
|
|
873
|
-
|
|
924
|
+
(0, import_fs2.writeFileSync)(
|
|
925
|
+
(0, import_path2.join)(agentDir, "metadata.json"),
|
|
926
|
+
JSON.stringify(metadata, null, 2)
|
|
927
|
+
);
|
|
928
|
+
(0, import_fs2.writeFileSync)(
|
|
929
|
+
(0, import_path2.join)(agentDir, "general_prompt.md"),
|
|
930
|
+
prompts2.general_prompt || ""
|
|
931
|
+
);
|
|
874
932
|
if (prompts2.begin_message) {
|
|
875
933
|
(0, import_fs2.writeFileSync)((0, import_path2.join)(agentDir, "begin_message.txt"), prompts2.begin_message);
|
|
876
934
|
}
|
|
@@ -895,9 +953,18 @@ function saveConversationFlowPrompts(agentDir, promptSource) {
|
|
|
895
953
|
version: prompts2.version,
|
|
896
954
|
pulled_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
897
955
|
};
|
|
898
|
-
(0, import_fs2.writeFileSync)(
|
|
899
|
-
|
|
900
|
-
|
|
956
|
+
(0, import_fs2.writeFileSync)(
|
|
957
|
+
(0, import_path2.join)(agentDir, "metadata.json"),
|
|
958
|
+
JSON.stringify(metadata, null, 2)
|
|
959
|
+
);
|
|
960
|
+
(0, import_fs2.writeFileSync)(
|
|
961
|
+
(0, import_path2.join)(agentDir, "global_prompt.md"),
|
|
962
|
+
prompts2.global_prompt || ""
|
|
963
|
+
);
|
|
964
|
+
(0, import_fs2.writeFileSync)(
|
|
965
|
+
(0, import_path2.join)(agentDir, "nodes.json"),
|
|
966
|
+
JSON.stringify(prompts2.nodes, null, 2)
|
|
967
|
+
);
|
|
901
968
|
}
|
|
902
969
|
function getFilesCreated(type, promptSource) {
|
|
903
970
|
const files = ["metadata.json"];
|
|
@@ -925,11 +992,15 @@ var import_fs3 = require("fs");
|
|
|
925
992
|
var import_path3 = require("path");
|
|
926
993
|
function loadLocalPrompts(agentId, agentDir) {
|
|
927
994
|
if (!(0, import_fs3.existsSync)(agentDir)) {
|
|
928
|
-
throw new Error(
|
|
995
|
+
throw new Error(
|
|
996
|
+
`Prompts directory not found: ${agentDir}. Run 'retell prompts pull ${agentId}' first.`
|
|
997
|
+
);
|
|
929
998
|
}
|
|
930
999
|
const metadataPath = (0, import_path3.join)(agentDir, "metadata.json");
|
|
931
1000
|
if (!(0, import_fs3.existsSync)(metadataPath)) {
|
|
932
|
-
throw new Error(
|
|
1001
|
+
throw new Error(
|
|
1002
|
+
`metadata.json not found in ${agentDir}. Directory may be corrupted.`
|
|
1003
|
+
);
|
|
933
1004
|
}
|
|
934
1005
|
let metadata;
|
|
935
1006
|
try {
|
|
@@ -955,7 +1026,9 @@ function loadLocalPrompts(agentId, agentDir) {
|
|
|
955
1026
|
prompts: prompts2
|
|
956
1027
|
};
|
|
957
1028
|
} else {
|
|
958
|
-
throw new Error(
|
|
1029
|
+
throw new Error(
|
|
1030
|
+
`Unknown agent type in metadata: ${metadata.type}`
|
|
1031
|
+
);
|
|
959
1032
|
}
|
|
960
1033
|
}
|
|
961
1034
|
function loadRetellLlmPrompts(agentDir) {
|
|
@@ -983,7 +1056,9 @@ function loadRetellLlmPrompts(agentDir) {
|
|
|
983
1056
|
const statesDir = (0, import_path3.join)(agentDir, "states");
|
|
984
1057
|
if ((0, import_fs3.existsSync)(statesDir)) {
|
|
985
1058
|
try {
|
|
986
|
-
const stateFiles = (0, import_fs3.readdirSync)(statesDir).filter(
|
|
1059
|
+
const stateFiles = (0, import_fs3.readdirSync)(statesDir).filter(
|
|
1060
|
+
(f) => f.endsWith(".md")
|
|
1061
|
+
);
|
|
987
1062
|
if (stateFiles.length > 0) {
|
|
988
1063
|
prompts2.states = stateFiles.map((file) => {
|
|
989
1064
|
const stateName = file.replace(".md", "");
|
|
@@ -991,12 +1066,16 @@ function loadRetellLlmPrompts(agentDir) {
|
|
|
991
1066
|
try {
|
|
992
1067
|
content = (0, import_fs3.readFileSync)((0, import_path3.join)(statesDir, file), "utf-8");
|
|
993
1068
|
} catch (error) {
|
|
994
|
-
throw new Error(
|
|
1069
|
+
throw new Error(
|
|
1070
|
+
`Failed to read state file ${file}: ${error.message}`
|
|
1071
|
+
);
|
|
995
1072
|
}
|
|
996
1073
|
const STATE_HEADER_REGEX = /^#\s+State:\s+(.+)$/m;
|
|
997
1074
|
const match = content.match(STATE_HEADER_REGEX);
|
|
998
1075
|
if (!match) {
|
|
999
|
-
throw new Error(
|
|
1076
|
+
throw new Error(
|
|
1077
|
+
`Invalid state file format in ${file}: missing "# State:" header`
|
|
1078
|
+
);
|
|
1000
1079
|
}
|
|
1001
1080
|
const statePrompt = content.replace(STATE_HEADER_REGEX, "").trim();
|
|
1002
1081
|
return {
|
|
@@ -1046,12 +1125,9 @@ function loadConversationFlowPrompts(agentDir) {
|
|
|
1046
1125
|
|
|
1047
1126
|
// src/services/prompt-diff.ts
|
|
1048
1127
|
function deepEqual(obj1, obj2, maxDepth = 100) {
|
|
1049
|
-
if (obj1 === obj2)
|
|
1050
|
-
|
|
1051
|
-
if (obj1
|
|
1052
|
-
return false;
|
|
1053
|
-
if (typeof obj1 !== "object" || typeof obj2 !== "object")
|
|
1054
|
-
return false;
|
|
1128
|
+
if (obj1 === obj2) return true;
|
|
1129
|
+
if (obj1 === null || obj2 === null) return false;
|
|
1130
|
+
if (typeof obj1 !== "object" || typeof obj2 !== "object") return false;
|
|
1055
1131
|
let currentDepth = 0;
|
|
1056
1132
|
const sortedStringify = (obj) => {
|
|
1057
1133
|
if (++currentDepth > maxDepth) {
|
|
@@ -1059,22 +1135,26 @@ function deepEqual(obj1, obj2, maxDepth = 100) {
|
|
|
1059
1135
|
`Maximum depth (${maxDepth}) exceeded during comparison. This may indicate a circular reference or extremely deep nesting.`
|
|
1060
1136
|
);
|
|
1061
1137
|
}
|
|
1062
|
-
if (obj === null)
|
|
1063
|
-
|
|
1064
|
-
if (typeof obj !== "object")
|
|
1065
|
-
return JSON.stringify(obj);
|
|
1138
|
+
if (obj === null) return "null";
|
|
1139
|
+
if (typeof obj !== "object") return JSON.stringify(obj);
|
|
1066
1140
|
if (Array.isArray(obj)) {
|
|
1067
1141
|
return "[" + obj.map(sortedStringify).join(",") + "]";
|
|
1068
1142
|
}
|
|
1069
1143
|
const keys = Object.keys(obj).sort();
|
|
1070
|
-
const pairs = keys.map(
|
|
1144
|
+
const pairs = keys.map(
|
|
1145
|
+
(k) => JSON.stringify(k) + ":" + sortedStringify(obj[k])
|
|
1146
|
+
);
|
|
1071
1147
|
return "{" + pairs.join(",") + "}";
|
|
1072
1148
|
};
|
|
1073
1149
|
try {
|
|
1074
1150
|
return sortedStringify(obj1) === sortedStringify(obj2);
|
|
1075
1151
|
} catch (error) {
|
|
1076
1152
|
if (error instanceof Error && error.message.includes("Maximum depth")) {
|
|
1077
|
-
console.warn(
|
|
1153
|
+
console.warn(
|
|
1154
|
+
"Warning:",
|
|
1155
|
+
error.message,
|
|
1156
|
+
"Falling back to reference equality."
|
|
1157
|
+
);
|
|
1078
1158
|
return obj1 === obj2;
|
|
1079
1159
|
}
|
|
1080
1160
|
throw error;
|
|
@@ -1130,8 +1210,12 @@ function generateRetellLlmDiff(agentId, localPrompts, remotePrompts) {
|
|
|
1130
1210
|
}
|
|
1131
1211
|
const localStates = localPrompts.prompts.states || [];
|
|
1132
1212
|
const remoteStates = remotePrompts.prompts.states || [];
|
|
1133
|
-
const localStatesMap = new Map(
|
|
1134
|
-
|
|
1213
|
+
const localStatesMap = new Map(
|
|
1214
|
+
localStates.map((s) => [s.name, s.state_prompt])
|
|
1215
|
+
);
|
|
1216
|
+
const remoteStatesMap = new Map(
|
|
1217
|
+
remoteStates.map((s) => [s.name, s.state_prompt])
|
|
1218
|
+
);
|
|
1135
1219
|
for (const [stateName, localPrompt] of localStatesMap) {
|
|
1136
1220
|
const remotePrompt = remoteStatesMap.get(stateName);
|
|
1137
1221
|
const fieldKey = `states.${stateName}`;
|
|
@@ -1219,7 +1303,9 @@ function generateConversationFlowDiff(agentId, localPrompts, remotePrompts) {
|
|
|
1219
1303
|
// src/commands/prompts/update.ts
|
|
1220
1304
|
function validateAgentId2(agentId) {
|
|
1221
1305
|
if (agentId.includes("..") || agentId.includes("/") || agentId.includes("\\")) {
|
|
1222
|
-
throw new Error(
|
|
1306
|
+
throw new Error(
|
|
1307
|
+
"Invalid agent ID: cannot contain path separators or traversal sequences"
|
|
1308
|
+
);
|
|
1223
1309
|
}
|
|
1224
1310
|
}
|
|
1225
1311
|
async function updatePromptsCommand(agentId, options) {
|
|
@@ -1242,7 +1328,9 @@ async function updatePromptsCommand(agentId, options) {
|
|
|
1242
1328
|
);
|
|
1243
1329
|
return;
|
|
1244
1330
|
}
|
|
1245
|
-
const metadata = JSON.parse(
|
|
1331
|
+
const metadata = JSON.parse(
|
|
1332
|
+
(0, import_fs4.readFileSync)(metadataPath, "utf-8")
|
|
1333
|
+
);
|
|
1246
1334
|
const promptSource = await resolvePromptSource(agentId);
|
|
1247
1335
|
if (promptSource.type === "custom-llm") {
|
|
1248
1336
|
outputError(promptSource.error, "CUSTOM_LLM_NOT_SUPPORTED");
|
|
@@ -1295,7 +1383,10 @@ async function updatePromptsCommand(agentId, options) {
|
|
|
1295
1383
|
note: `Run 'retell agent-publish ${agentId}' to publish changes to production`
|
|
1296
1384
|
});
|
|
1297
1385
|
} else if (promptSource.type === "conversation-flow" && localPrompts.type === "conversation-flow") {
|
|
1298
|
-
await client.conversationFlow.update(
|
|
1386
|
+
await client.conversationFlow.update(
|
|
1387
|
+
promptSource.flowId,
|
|
1388
|
+
localPrompts.prompts
|
|
1389
|
+
);
|
|
1299
1390
|
outputJson({
|
|
1300
1391
|
message: "Prompts updated successfully (draft version)",
|
|
1301
1392
|
agent_id: agentId,
|
|
@@ -1318,7 +1409,9 @@ async function updatePromptsCommand(agentId, options) {
|
|
|
1318
1409
|
var import_path5 = require("path");
|
|
1319
1410
|
function validateAgentId3(agentId) {
|
|
1320
1411
|
if (agentId.includes("..") || agentId.includes("/") || agentId.includes("\\")) {
|
|
1321
|
-
throw new Error(
|
|
1412
|
+
throw new Error(
|
|
1413
|
+
"Invalid agent ID: cannot contain path separators or traversal sequences"
|
|
1414
|
+
);
|
|
1322
1415
|
}
|
|
1323
1416
|
}
|
|
1324
1417
|
async function diffPromptsCommand(agentId, options) {
|
|
@@ -1391,7 +1484,10 @@ async function resolveToolsSource(agentId) {
|
|
|
1391
1484
|
}
|
|
1392
1485
|
}
|
|
1393
1486
|
}
|
|
1394
|
-
const stateToolCount = Object.values(stateTools).reduce(
|
|
1487
|
+
const stateToolCount = Object.values(stateTools).reduce(
|
|
1488
|
+
(sum, t) => sum + t.length,
|
|
1489
|
+
0
|
|
1490
|
+
);
|
|
1395
1491
|
const totalCount = generalTools.length + stateToolCount;
|
|
1396
1492
|
return {
|
|
1397
1493
|
type: "retell-llm",
|
|
@@ -1417,7 +1513,10 @@ async function resolveToolsSource(agentId) {
|
|
|
1417
1513
|
}
|
|
1418
1514
|
}
|
|
1419
1515
|
}
|
|
1420
|
-
const componentToolCount = Object.values(componentTools).reduce(
|
|
1516
|
+
const componentToolCount = Object.values(componentTools).reduce(
|
|
1517
|
+
(sum, t) => sum + t.length,
|
|
1518
|
+
0
|
|
1519
|
+
);
|
|
1421
1520
|
const totalCount = flowTools.length + componentToolCount;
|
|
1422
1521
|
return {
|
|
1423
1522
|
type: "conversation-flow",
|
|
@@ -1644,7 +1743,10 @@ async function listToolsCommand(agentId, options) {
|
|
|
1644
1743
|
}
|
|
1645
1744
|
}
|
|
1646
1745
|
if (options.fields) {
|
|
1647
|
-
const filtered = filterFields(
|
|
1746
|
+
const filtered = filterFields(
|
|
1747
|
+
output,
|
|
1748
|
+
options.fields.split(",").map((f) => f.trim())
|
|
1749
|
+
);
|
|
1648
1750
|
outputJson(filtered);
|
|
1649
1751
|
} else {
|
|
1650
1752
|
outputJson(output);
|
|
@@ -1680,7 +1782,10 @@ async function getToolCommand(agentId, toolName, options) {
|
|
|
1680
1782
|
tool: result.tool.tool
|
|
1681
1783
|
};
|
|
1682
1784
|
if (options.fields) {
|
|
1683
|
-
const filtered = filterFields(
|
|
1785
|
+
const filtered = filterFields(
|
|
1786
|
+
output,
|
|
1787
|
+
options.fields.split(",").map((f) => f.trim())
|
|
1788
|
+
);
|
|
1684
1789
|
outputJson(filtered);
|
|
1685
1790
|
} else {
|
|
1686
1791
|
outputJson(output);
|
|
@@ -1704,9 +1809,15 @@ async function addToolCommand(agentId, options) {
|
|
|
1704
1809
|
tool = JSON.parse(content);
|
|
1705
1810
|
} catch (error) {
|
|
1706
1811
|
if (error instanceof SyntaxError) {
|
|
1707
|
-
outputError(
|
|
1812
|
+
outputError(
|
|
1813
|
+
`Invalid JSON in tool file: ${error.message}`,
|
|
1814
|
+
"INVALID_JSON"
|
|
1815
|
+
);
|
|
1708
1816
|
} else {
|
|
1709
|
-
outputError(
|
|
1817
|
+
outputError(
|
|
1818
|
+
`Error reading tool file: ${error.message}`,
|
|
1819
|
+
"FILE_READ_ERROR"
|
|
1820
|
+
);
|
|
1710
1821
|
}
|
|
1711
1822
|
return;
|
|
1712
1823
|
}
|
|
@@ -1761,7 +1872,10 @@ async function addToolCommand(agentId, options) {
|
|
|
1761
1872
|
const updatedStates = [...states];
|
|
1762
1873
|
const stateTools = [...updatedStates[stateIndex].tools ?? []];
|
|
1763
1874
|
stateTools.push(tool);
|
|
1764
|
-
updatedStates[stateIndex] = {
|
|
1875
|
+
updatedStates[stateIndex] = {
|
|
1876
|
+
...updatedStates[stateIndex],
|
|
1877
|
+
tools: stateTools
|
|
1878
|
+
};
|
|
1765
1879
|
await client.llm.update(source.llmId, { states: updatedStates });
|
|
1766
1880
|
const output = {
|
|
1767
1881
|
message: "Tool added successfully (draft version)",
|
|
@@ -1776,7 +1890,9 @@ async function addToolCommand(agentId, options) {
|
|
|
1776
1890
|
} else {
|
|
1777
1891
|
const generalTools = [...llm.general_tools ?? []];
|
|
1778
1892
|
generalTools.push(tool);
|
|
1779
|
-
await client.llm.update(source.llmId, {
|
|
1893
|
+
await client.llm.update(source.llmId, {
|
|
1894
|
+
general_tools: generalTools
|
|
1895
|
+
});
|
|
1780
1896
|
const output = {
|
|
1781
1897
|
message: "Tool added successfully (draft version)",
|
|
1782
1898
|
agent_id: agentId,
|
|
@@ -1806,7 +1922,9 @@ async function addToolCommand(agentId, options) {
|
|
|
1806
1922
|
const flow = await client.conversationFlow.retrieve(source.flowId);
|
|
1807
1923
|
if (options.component) {
|
|
1808
1924
|
const components = flow.components ?? [];
|
|
1809
|
-
const compIndex = components.findIndex(
|
|
1925
|
+
const compIndex = components.findIndex(
|
|
1926
|
+
(c) => c.name === options.component
|
|
1927
|
+
);
|
|
1810
1928
|
if (compIndex === -1) {
|
|
1811
1929
|
outputError(
|
|
1812
1930
|
`Component '${options.component}' not found. Available components: ${components.map((c) => c.name).join(", ") || "none"}`,
|
|
@@ -1817,8 +1935,13 @@ async function addToolCommand(agentId, options) {
|
|
|
1817
1935
|
const updatedComponents = [...components];
|
|
1818
1936
|
const compTools = [...updatedComponents[compIndex].tools ?? []];
|
|
1819
1937
|
compTools.push(tool);
|
|
1820
|
-
updatedComponents[compIndex] = {
|
|
1821
|
-
|
|
1938
|
+
updatedComponents[compIndex] = {
|
|
1939
|
+
...updatedComponents[compIndex],
|
|
1940
|
+
tools: compTools
|
|
1941
|
+
};
|
|
1942
|
+
await client.conversationFlow.update(source.flowId, {
|
|
1943
|
+
components: updatedComponents
|
|
1944
|
+
});
|
|
1822
1945
|
const output = {
|
|
1823
1946
|
message: "Tool added successfully (draft version)",
|
|
1824
1947
|
agent_id: agentId,
|
|
@@ -1832,7 +1955,9 @@ async function addToolCommand(agentId, options) {
|
|
|
1832
1955
|
} else {
|
|
1833
1956
|
const flowTools = [...flow.tools ?? []];
|
|
1834
1957
|
flowTools.push(tool);
|
|
1835
|
-
await client.conversationFlow.update(source.flowId, {
|
|
1958
|
+
await client.conversationFlow.update(source.flowId, {
|
|
1959
|
+
tools: flowTools
|
|
1960
|
+
});
|
|
1836
1961
|
const output = {
|
|
1837
1962
|
message: "Tool added successfully (draft version)",
|
|
1838
1963
|
agent_id: agentId,
|
|
@@ -1868,9 +1993,15 @@ async function updateToolCommand(agentId, toolName, options) {
|
|
|
1868
1993
|
newTool = JSON.parse(content);
|
|
1869
1994
|
} catch (error) {
|
|
1870
1995
|
if (error instanceof SyntaxError) {
|
|
1871
|
-
outputError(
|
|
1996
|
+
outputError(
|
|
1997
|
+
`Invalid JSON in tool file: ${error.message}`,
|
|
1998
|
+
"INVALID_JSON"
|
|
1999
|
+
);
|
|
1872
2000
|
} else {
|
|
1873
|
-
outputError(
|
|
2001
|
+
outputError(
|
|
2002
|
+
`Error reading tool file: ${error.message}`,
|
|
2003
|
+
"FILE_READ_ERROR"
|
|
2004
|
+
);
|
|
1874
2005
|
}
|
|
1875
2006
|
return;
|
|
1876
2007
|
}
|
|
@@ -1925,11 +2056,17 @@ async function updateToolCommand(agentId, toolName, options) {
|
|
|
1925
2056
|
const stateTools = [...updatedStates[stateIndex].tools ?? []];
|
|
1926
2057
|
const toolIndex = stateTools.findIndex((t) => t.name === toolName);
|
|
1927
2058
|
if (toolIndex === -1) {
|
|
1928
|
-
outputError(
|
|
2059
|
+
outputError(
|
|
2060
|
+
`Tool '${toolName}' not found in state '${stateName}'`,
|
|
2061
|
+
"TOOL_NOT_FOUND"
|
|
2062
|
+
);
|
|
1929
2063
|
return;
|
|
1930
2064
|
}
|
|
1931
2065
|
stateTools[toolIndex] = newTool;
|
|
1932
|
-
updatedStates[stateIndex] = {
|
|
2066
|
+
updatedStates[stateIndex] = {
|
|
2067
|
+
...updatedStates[stateIndex],
|
|
2068
|
+
tools: stateTools
|
|
2069
|
+
};
|
|
1933
2070
|
await client.llm.update(source.llmId, { states: updatedStates });
|
|
1934
2071
|
const output = {
|
|
1935
2072
|
message: "Tool updated successfully (draft version)",
|
|
@@ -1943,13 +2080,20 @@ async function updateToolCommand(agentId, toolName, options) {
|
|
|
1943
2080
|
outputJson(output);
|
|
1944
2081
|
} else {
|
|
1945
2082
|
const generalTools = [...llm.general_tools ?? []];
|
|
1946
|
-
const toolIndex = generalTools.findIndex(
|
|
2083
|
+
const toolIndex = generalTools.findIndex(
|
|
2084
|
+
(t) => t.name === toolName
|
|
2085
|
+
);
|
|
1947
2086
|
if (toolIndex === -1) {
|
|
1948
|
-
outputError(
|
|
2087
|
+
outputError(
|
|
2088
|
+
`Tool '${toolName}' not found in general tools`,
|
|
2089
|
+
"TOOL_NOT_FOUND"
|
|
2090
|
+
);
|
|
1949
2091
|
return;
|
|
1950
2092
|
}
|
|
1951
2093
|
generalTools[toolIndex] = newTool;
|
|
1952
|
-
await client.llm.update(source.llmId, {
|
|
2094
|
+
await client.llm.update(source.llmId, {
|
|
2095
|
+
general_tools: generalTools
|
|
2096
|
+
});
|
|
1953
2097
|
const output = {
|
|
1954
2098
|
message: "Tool updated successfully (draft version)",
|
|
1955
2099
|
agent_id: agentId,
|
|
@@ -1981,19 +2125,30 @@ async function updateToolCommand(agentId, toolName, options) {
|
|
|
1981
2125
|
const components = flow.components ?? [];
|
|
1982
2126
|
const compIndex = components.findIndex((c) => c.name === componentId);
|
|
1983
2127
|
if (compIndex === -1) {
|
|
1984
|
-
outputError(
|
|
2128
|
+
outputError(
|
|
2129
|
+
`Component '${componentId}' not found`,
|
|
2130
|
+
"COMPONENT_NOT_FOUND"
|
|
2131
|
+
);
|
|
1985
2132
|
return;
|
|
1986
2133
|
}
|
|
1987
2134
|
const updatedComponents = [...components];
|
|
1988
2135
|
const compTools = [...updatedComponents[compIndex].tools ?? []];
|
|
1989
2136
|
const toolIndex = compTools.findIndex((t) => t.name === toolName);
|
|
1990
2137
|
if (toolIndex === -1) {
|
|
1991
|
-
outputError(
|
|
2138
|
+
outputError(
|
|
2139
|
+
`Tool '${toolName}' not found in component '${componentId}'`,
|
|
2140
|
+
"TOOL_NOT_FOUND"
|
|
2141
|
+
);
|
|
1992
2142
|
return;
|
|
1993
2143
|
}
|
|
1994
2144
|
compTools[toolIndex] = newTool;
|
|
1995
|
-
updatedComponents[compIndex] = {
|
|
1996
|
-
|
|
2145
|
+
updatedComponents[compIndex] = {
|
|
2146
|
+
...updatedComponents[compIndex],
|
|
2147
|
+
tools: compTools
|
|
2148
|
+
};
|
|
2149
|
+
await client.conversationFlow.update(source.flowId, {
|
|
2150
|
+
components: updatedComponents
|
|
2151
|
+
});
|
|
1997
2152
|
const output = {
|
|
1998
2153
|
message: "Tool updated successfully (draft version)",
|
|
1999
2154
|
agent_id: agentId,
|
|
@@ -2008,11 +2163,16 @@ async function updateToolCommand(agentId, toolName, options) {
|
|
|
2008
2163
|
const flowTools = [...flow.tools ?? []];
|
|
2009
2164
|
const toolIndex = flowTools.findIndex((t) => t.name === toolName);
|
|
2010
2165
|
if (toolIndex === -1) {
|
|
2011
|
-
outputError(
|
|
2166
|
+
outputError(
|
|
2167
|
+
`Tool '${toolName}' not found in flow tools`,
|
|
2168
|
+
"TOOL_NOT_FOUND"
|
|
2169
|
+
);
|
|
2012
2170
|
return;
|
|
2013
2171
|
}
|
|
2014
2172
|
flowTools[toolIndex] = newTool;
|
|
2015
|
-
await client.conversationFlow.update(source.flowId, {
|
|
2173
|
+
await client.conversationFlow.update(source.flowId, {
|
|
2174
|
+
tools: flowTools
|
|
2175
|
+
});
|
|
2016
2176
|
const output = {
|
|
2017
2177
|
message: "Tool updated successfully (draft version)",
|
|
2018
2178
|
agent_id: agentId,
|
|
@@ -2079,11 +2239,17 @@ async function removeToolCommand(agentId, toolName, options) {
|
|
|
2079
2239
|
const stateTools = [...updatedStates[stateIndex].tools ?? []];
|
|
2080
2240
|
const toolIndex = stateTools.findIndex((t) => t.name === toolName);
|
|
2081
2241
|
if (toolIndex === -1) {
|
|
2082
|
-
outputError(
|
|
2242
|
+
outputError(
|
|
2243
|
+
`Tool '${toolName}' not found in state '${stateName}'`,
|
|
2244
|
+
"TOOL_NOT_FOUND"
|
|
2245
|
+
);
|
|
2083
2246
|
return;
|
|
2084
2247
|
}
|
|
2085
2248
|
stateTools.splice(toolIndex, 1);
|
|
2086
|
-
updatedStates[stateIndex] = {
|
|
2249
|
+
updatedStates[stateIndex] = {
|
|
2250
|
+
...updatedStates[stateIndex],
|
|
2251
|
+
tools: stateTools
|
|
2252
|
+
};
|
|
2087
2253
|
await client.llm.update(source.llmId, { states: updatedStates });
|
|
2088
2254
|
const output = {
|
|
2089
2255
|
message: "Tool removed successfully (draft version)",
|
|
@@ -2097,13 +2263,20 @@ async function removeToolCommand(agentId, toolName, options) {
|
|
|
2097
2263
|
outputJson(output);
|
|
2098
2264
|
} else {
|
|
2099
2265
|
const generalTools = [...llm.general_tools ?? []];
|
|
2100
|
-
const toolIndex = generalTools.findIndex(
|
|
2266
|
+
const toolIndex = generalTools.findIndex(
|
|
2267
|
+
(t) => t.name === toolName
|
|
2268
|
+
);
|
|
2101
2269
|
if (toolIndex === -1) {
|
|
2102
|
-
outputError(
|
|
2270
|
+
outputError(
|
|
2271
|
+
`Tool '${toolName}' not found in general tools`,
|
|
2272
|
+
"TOOL_NOT_FOUND"
|
|
2273
|
+
);
|
|
2103
2274
|
return;
|
|
2104
2275
|
}
|
|
2105
2276
|
generalTools.splice(toolIndex, 1);
|
|
2106
|
-
await client.llm.update(source.llmId, {
|
|
2277
|
+
await client.llm.update(source.llmId, {
|
|
2278
|
+
general_tools: generalTools
|
|
2279
|
+
});
|
|
2107
2280
|
const output = {
|
|
2108
2281
|
message: "Tool removed successfully (draft version)",
|
|
2109
2282
|
agent_id: agentId,
|
|
@@ -2134,19 +2307,30 @@ async function removeToolCommand(agentId, toolName, options) {
|
|
|
2134
2307
|
const components = flow.components ?? [];
|
|
2135
2308
|
const compIndex = components.findIndex((c) => c.name === componentId);
|
|
2136
2309
|
if (compIndex === -1) {
|
|
2137
|
-
outputError(
|
|
2310
|
+
outputError(
|
|
2311
|
+
`Component '${componentId}' not found`,
|
|
2312
|
+
"COMPONENT_NOT_FOUND"
|
|
2313
|
+
);
|
|
2138
2314
|
return;
|
|
2139
2315
|
}
|
|
2140
2316
|
const updatedComponents = [...components];
|
|
2141
2317
|
const compTools = [...updatedComponents[compIndex].tools ?? []];
|
|
2142
2318
|
const toolIndex = compTools.findIndex((t) => t.name === toolName);
|
|
2143
2319
|
if (toolIndex === -1) {
|
|
2144
|
-
outputError(
|
|
2320
|
+
outputError(
|
|
2321
|
+
`Tool '${toolName}' not found in component '${componentId}'`,
|
|
2322
|
+
"TOOL_NOT_FOUND"
|
|
2323
|
+
);
|
|
2145
2324
|
return;
|
|
2146
2325
|
}
|
|
2147
2326
|
compTools.splice(toolIndex, 1);
|
|
2148
|
-
updatedComponents[compIndex] = {
|
|
2149
|
-
|
|
2327
|
+
updatedComponents[compIndex] = {
|
|
2328
|
+
...updatedComponents[compIndex],
|
|
2329
|
+
tools: compTools
|
|
2330
|
+
};
|
|
2331
|
+
await client.conversationFlow.update(source.flowId, {
|
|
2332
|
+
components: updatedComponents
|
|
2333
|
+
});
|
|
2150
2334
|
const output = {
|
|
2151
2335
|
message: "Tool removed successfully (draft version)",
|
|
2152
2336
|
agent_id: agentId,
|
|
@@ -2161,11 +2345,16 @@ async function removeToolCommand(agentId, toolName, options) {
|
|
|
2161
2345
|
const flowTools = [...flow.tools ?? []];
|
|
2162
2346
|
const toolIndex = flowTools.findIndex((t) => t.name === toolName);
|
|
2163
2347
|
if (toolIndex === -1) {
|
|
2164
|
-
outputError(
|
|
2348
|
+
outputError(
|
|
2349
|
+
`Tool '${toolName}' not found in flow tools`,
|
|
2350
|
+
"TOOL_NOT_FOUND"
|
|
2351
|
+
);
|
|
2165
2352
|
return;
|
|
2166
2353
|
}
|
|
2167
2354
|
flowTools.splice(toolIndex, 1);
|
|
2168
|
-
await client.conversationFlow.update(source.flowId, {
|
|
2355
|
+
await client.conversationFlow.update(source.flowId, {
|
|
2356
|
+
tools: flowTools
|
|
2357
|
+
});
|
|
2169
2358
|
const output = {
|
|
2170
2359
|
message: "Tool removed successfully (draft version)",
|
|
2171
2360
|
agent_id: agentId,
|
|
@@ -2232,7 +2421,11 @@ async function exportToolsCommand(agentId, options) {
|
|
|
2232
2421
|
}
|
|
2233
2422
|
if (options.output) {
|
|
2234
2423
|
try {
|
|
2235
|
-
(0, import_fs7.writeFileSync)(
|
|
2424
|
+
(0, import_fs7.writeFileSync)(
|
|
2425
|
+
options.output,
|
|
2426
|
+
JSON.stringify(exportData, null, 2),
|
|
2427
|
+
"utf-8"
|
|
2428
|
+
);
|
|
2236
2429
|
outputJson({
|
|
2237
2430
|
message: "Tools exported successfully",
|
|
2238
2431
|
agent_id: agentId,
|
|
@@ -2243,7 +2436,10 @@ async function exportToolsCommand(agentId, options) {
|
|
|
2243
2436
|
});
|
|
2244
2437
|
} catch (error) {
|
|
2245
2438
|
if (error.code === "EACCES") {
|
|
2246
|
-
outputError(
|
|
2439
|
+
outputError(
|
|
2440
|
+
`Permission denied writing to: ${options.output}`,
|
|
2441
|
+
"PERMISSION_DENIED"
|
|
2442
|
+
);
|
|
2247
2443
|
} else if (error.code === "ENOSPC") {
|
|
2248
2444
|
outputError("No space left on device", "NO_SPACE");
|
|
2249
2445
|
} else {
|
|
@@ -2272,14 +2468,23 @@ async function importToolsCommand(agentId, options) {
|
|
|
2272
2468
|
importData = JSON.parse(content);
|
|
2273
2469
|
} catch (error) {
|
|
2274
2470
|
if (error instanceof SyntaxError) {
|
|
2275
|
-
outputError(
|
|
2471
|
+
outputError(
|
|
2472
|
+
`Invalid JSON in import file: ${error.message}`,
|
|
2473
|
+
"INVALID_JSON"
|
|
2474
|
+
);
|
|
2276
2475
|
} else {
|
|
2277
|
-
outputError(
|
|
2476
|
+
outputError(
|
|
2477
|
+
`Error reading import file: ${error.message}`,
|
|
2478
|
+
"FILE_READ_ERROR"
|
|
2479
|
+
);
|
|
2278
2480
|
}
|
|
2279
2481
|
return;
|
|
2280
2482
|
}
|
|
2281
2483
|
if (!importData.tools) {
|
|
2282
|
-
outputError(
|
|
2484
|
+
outputError(
|
|
2485
|
+
'Import file must contain a "tools" object',
|
|
2486
|
+
"INVALID_IMPORT"
|
|
2487
|
+
);
|
|
2283
2488
|
return;
|
|
2284
2489
|
}
|
|
2285
2490
|
const source = await resolveToolsSource(agentId);
|
|
@@ -2307,7 +2512,9 @@ async function importToolsCommand(agentId, options) {
|
|
|
2307
2512
|
for (const tool of importData.tools.general) {
|
|
2308
2513
|
if (existingNames.has(tool.name)) {
|
|
2309
2514
|
if (options.replace) {
|
|
2310
|
-
const idx = generalTools.findIndex(
|
|
2515
|
+
const idx = generalTools.findIndex(
|
|
2516
|
+
(t) => t.name === tool.name
|
|
2517
|
+
);
|
|
2311
2518
|
if (idx !== -1) {
|
|
2312
2519
|
generalTools[idx] = tool;
|
|
2313
2520
|
toolsReplaced.push(tool.name);
|
|
@@ -2323,7 +2530,9 @@ async function importToolsCommand(agentId, options) {
|
|
|
2323
2530
|
}
|
|
2324
2531
|
}
|
|
2325
2532
|
if (importData.tools.states) {
|
|
2326
|
-
for (const [stateName, tools2] of Object.entries(
|
|
2533
|
+
for (const [stateName, tools2] of Object.entries(
|
|
2534
|
+
importData.tools.states
|
|
2535
|
+
)) {
|
|
2327
2536
|
const stateIndex = states.findIndex((s) => s.name === stateName);
|
|
2328
2537
|
if (stateIndex === -1) {
|
|
2329
2538
|
outputError(
|
|
@@ -2336,7 +2545,9 @@ async function importToolsCommand(agentId, options) {
|
|
|
2336
2545
|
for (const tool of tools2) {
|
|
2337
2546
|
if (existingNames.has(tool.name)) {
|
|
2338
2547
|
if (options.replace) {
|
|
2339
|
-
const idx = stateTools.findIndex(
|
|
2548
|
+
const idx = stateTools.findIndex(
|
|
2549
|
+
(t) => t.name === tool.name
|
|
2550
|
+
);
|
|
2340
2551
|
if (idx !== -1) {
|
|
2341
2552
|
stateTools[idx] = tool;
|
|
2342
2553
|
toolsReplaced.push(`${stateName}/${tool.name}`);
|
|
@@ -2350,7 +2561,10 @@ async function importToolsCommand(agentId, options) {
|
|
|
2350
2561
|
existingNames.add(tool.name);
|
|
2351
2562
|
}
|
|
2352
2563
|
}
|
|
2353
|
-
states[stateIndex] = {
|
|
2564
|
+
states[stateIndex] = {
|
|
2565
|
+
...states[stateIndex],
|
|
2566
|
+
tools: stateTools
|
|
2567
|
+
};
|
|
2354
2568
|
}
|
|
2355
2569
|
}
|
|
2356
2570
|
if (options.dryRun) {
|
|
@@ -2410,7 +2624,9 @@ async function importToolsCommand(agentId, options) {
|
|
|
2410
2624
|
}
|
|
2411
2625
|
}
|
|
2412
2626
|
if (importData.tools.components) {
|
|
2413
|
-
for (const [compId, tools2] of Object.entries(
|
|
2627
|
+
for (const [compId, tools2] of Object.entries(
|
|
2628
|
+
importData.tools.components
|
|
2629
|
+
)) {
|
|
2414
2630
|
const compIndex = components.findIndex((c) => c.name === compId);
|
|
2415
2631
|
if (compIndex === -1) {
|
|
2416
2632
|
outputError(
|
|
@@ -2423,7 +2639,9 @@ async function importToolsCommand(agentId, options) {
|
|
|
2423
2639
|
for (const tool of tools2) {
|
|
2424
2640
|
if (existingNames.has(tool.name)) {
|
|
2425
2641
|
if (options.replace) {
|
|
2426
|
-
const idx = compTools.findIndex(
|
|
2642
|
+
const idx = compTools.findIndex(
|
|
2643
|
+
(t) => t.name === tool.name
|
|
2644
|
+
);
|
|
2427
2645
|
if (idx !== -1) {
|
|
2428
2646
|
compTools[idx] = tool;
|
|
2429
2647
|
toolsReplaced.push(`${compId}/${tool.name}`);
|
|
@@ -2437,7 +2655,10 @@ async function importToolsCommand(agentId, options) {
|
|
|
2437
2655
|
existingNames.add(tool.name);
|
|
2438
2656
|
}
|
|
2439
2657
|
}
|
|
2440
|
-
components[compIndex] = {
|
|
2658
|
+
components[compIndex] = {
|
|
2659
|
+
...components[compIndex],
|
|
2660
|
+
tools: compTools
|
|
2661
|
+
};
|
|
2441
2662
|
}
|
|
2442
2663
|
}
|
|
2443
2664
|
if (options.dryRun) {
|
|
@@ -2483,7 +2704,7 @@ async function apiRequest(method, path, body) {
|
|
|
2483
2704
|
const response = await fetch(`${BASE_URL}${path}`, {
|
|
2484
2705
|
method,
|
|
2485
2706
|
headers: {
|
|
2486
|
-
|
|
2707
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
2487
2708
|
"Content-Type": "application/json"
|
|
2488
2709
|
},
|
|
2489
2710
|
body: body ? JSON.stringify(body) : void 0
|
|
@@ -2566,42 +2787,35 @@ async function listBatchTests(responseEngine) {
|
|
|
2566
2787
|
);
|
|
2567
2788
|
}
|
|
2568
2789
|
async function getBatchTest(batchJobId) {
|
|
2569
|
-
return apiRequest(
|
|
2570
|
-
"GET",
|
|
2571
|
-
`/get-batch-test/${batchJobId}`
|
|
2572
|
-
);
|
|
2790
|
+
return apiRequest("GET", `/get-batch-test/${batchJobId}`);
|
|
2573
2791
|
}
|
|
2574
2792
|
async function createBatchTest(params) {
|
|
2575
|
-
return apiRequest(
|
|
2576
|
-
"POST",
|
|
2577
|
-
"/create-batch-test",
|
|
2578
|
-
params
|
|
2579
|
-
);
|
|
2793
|
+
return apiRequest("POST", "/create-batch-test", params);
|
|
2580
2794
|
}
|
|
2581
2795
|
async function listTestRuns(batchJobId) {
|
|
2582
|
-
return apiRequest(
|
|
2583
|
-
"GET",
|
|
2584
|
-
`/list-test-runs/${batchJobId}`
|
|
2585
|
-
);
|
|
2796
|
+
return apiRequest("GET", `/list-test-runs/${batchJobId}`);
|
|
2586
2797
|
}
|
|
2587
2798
|
async function getTestRun(testRunId) {
|
|
2588
|
-
return apiRequest(
|
|
2589
|
-
"GET",
|
|
2590
|
-
`/get-test-run/${testRunId}`
|
|
2591
|
-
);
|
|
2799
|
+
return apiRequest("GET", `/get-test-run/${testRunId}`);
|
|
2592
2800
|
}
|
|
2593
2801
|
|
|
2594
2802
|
// src/commands/tests/cases/list.ts
|
|
2595
2803
|
function buildResponseEngine(options) {
|
|
2596
2804
|
if (options.type === "retell-llm") {
|
|
2597
2805
|
if (!options.llmId) {
|
|
2598
|
-
outputError(
|
|
2806
|
+
outputError(
|
|
2807
|
+
"--llm-id is required when type is retell-llm",
|
|
2808
|
+
"MISSING_PARAMETER"
|
|
2809
|
+
);
|
|
2599
2810
|
return null;
|
|
2600
2811
|
}
|
|
2601
2812
|
return { type: "retell-llm", llm_id: options.llmId };
|
|
2602
2813
|
} else {
|
|
2603
2814
|
if (!options.flowId) {
|
|
2604
|
-
outputError(
|
|
2815
|
+
outputError(
|
|
2816
|
+
"--flow-id is required when type is conversation-flow",
|
|
2817
|
+
"MISSING_PARAMETER"
|
|
2818
|
+
);
|
|
2605
2819
|
return null;
|
|
2606
2820
|
}
|
|
2607
2821
|
return { type: "conversation-flow", conversation_flow_id: options.flowId };
|
|
@@ -2610,8 +2824,7 @@ function buildResponseEngine(options) {
|
|
|
2610
2824
|
async function listTestCasesCommand(options) {
|
|
2611
2825
|
try {
|
|
2612
2826
|
const responseEngine = buildResponseEngine(options);
|
|
2613
|
-
if (!responseEngine)
|
|
2614
|
-
return;
|
|
2827
|
+
if (!responseEngine) return;
|
|
2615
2828
|
const testCases = await listTestCaseDefinitions(responseEngine);
|
|
2616
2829
|
const output = {
|
|
2617
2830
|
response_engine: responseEngine,
|
|
@@ -2619,7 +2832,10 @@ async function listTestCasesCommand(options) {
|
|
|
2619
2832
|
total_count: (testCases || []).length
|
|
2620
2833
|
};
|
|
2621
2834
|
if (options.fields) {
|
|
2622
|
-
const filtered = filterFields(
|
|
2835
|
+
const filtered = filterFields(
|
|
2836
|
+
output,
|
|
2837
|
+
options.fields.split(",").map((f) => f.trim())
|
|
2838
|
+
);
|
|
2623
2839
|
outputJson(filtered);
|
|
2624
2840
|
} else {
|
|
2625
2841
|
outputJson(output);
|
|
@@ -2634,7 +2850,10 @@ async function getTestCaseCommand(testCaseDefinitionId, options) {
|
|
|
2634
2850
|
try {
|
|
2635
2851
|
const testCase = await getTestCaseDefinition(testCaseDefinitionId);
|
|
2636
2852
|
if (options.fields) {
|
|
2637
|
-
const filtered = filterFields(
|
|
2853
|
+
const filtered = filterFields(
|
|
2854
|
+
testCase,
|
|
2855
|
+
options.fields.split(",").map((f) => f.trim())
|
|
2856
|
+
);
|
|
2638
2857
|
outputJson(filtered);
|
|
2639
2858
|
} else {
|
|
2640
2859
|
outputJson(testCase);
|
|
@@ -2648,21 +2867,33 @@ async function getTestCaseCommand(testCaseDefinitionId, options) {
|
|
|
2648
2867
|
var import_fs9 = require("fs");
|
|
2649
2868
|
function buildResponseEngine2(options) {
|
|
2650
2869
|
if (options.llmId && options.flowId) {
|
|
2651
|
-
outputError(
|
|
2870
|
+
outputError(
|
|
2871
|
+
"Cannot specify both --llm-id and --flow-id",
|
|
2872
|
+
"INVALID_PARAMETERS"
|
|
2873
|
+
);
|
|
2652
2874
|
return null;
|
|
2653
2875
|
}
|
|
2654
2876
|
if (!options.llmId && !options.flowId) {
|
|
2655
|
-
outputError(
|
|
2877
|
+
outputError(
|
|
2878
|
+
"Either --llm-id or --flow-id is required",
|
|
2879
|
+
"MISSING_PARAMETER"
|
|
2880
|
+
);
|
|
2656
2881
|
return null;
|
|
2657
2882
|
}
|
|
2658
2883
|
if (options.llmId) {
|
|
2659
|
-
const engine = {
|
|
2884
|
+
const engine = {
|
|
2885
|
+
type: "retell-llm",
|
|
2886
|
+
llm_id: options.llmId
|
|
2887
|
+
};
|
|
2660
2888
|
if (options.version !== void 0) {
|
|
2661
2889
|
engine.version = options.version;
|
|
2662
2890
|
}
|
|
2663
2891
|
return engine;
|
|
2664
2892
|
} else {
|
|
2665
|
-
const engine = {
|
|
2893
|
+
const engine = {
|
|
2894
|
+
type: "conversation-flow",
|
|
2895
|
+
conversation_flow_id: options.flowId
|
|
2896
|
+
};
|
|
2666
2897
|
if (options.version !== void 0) {
|
|
2667
2898
|
engine.version = options.version;
|
|
2668
2899
|
}
|
|
@@ -2672,7 +2903,10 @@ function buildResponseEngine2(options) {
|
|
|
2672
2903
|
async function createTestCaseCommand(options) {
|
|
2673
2904
|
try {
|
|
2674
2905
|
if (!(0, import_fs9.existsSync)(options.file)) {
|
|
2675
|
-
outputError(
|
|
2906
|
+
outputError(
|
|
2907
|
+
`Test case file not found: ${options.file}`,
|
|
2908
|
+
"FILE_NOT_FOUND"
|
|
2909
|
+
);
|
|
2676
2910
|
return;
|
|
2677
2911
|
}
|
|
2678
2912
|
let input;
|
|
@@ -2681,9 +2915,15 @@ async function createTestCaseCommand(options) {
|
|
|
2681
2915
|
input = JSON.parse(content);
|
|
2682
2916
|
} catch (error) {
|
|
2683
2917
|
if (error instanceof SyntaxError) {
|
|
2684
|
-
outputError(
|
|
2918
|
+
outputError(
|
|
2919
|
+
`Invalid JSON in test case file: ${error.message}`,
|
|
2920
|
+
"INVALID_JSON"
|
|
2921
|
+
);
|
|
2685
2922
|
} else {
|
|
2686
|
-
outputError(
|
|
2923
|
+
outputError(
|
|
2924
|
+
`Error reading test case file: ${error.message}`,
|
|
2925
|
+
"FILE_READ_ERROR"
|
|
2926
|
+
);
|
|
2687
2927
|
}
|
|
2688
2928
|
return;
|
|
2689
2929
|
}
|
|
@@ -2692,14 +2932,16 @@ async function createTestCaseCommand(options) {
|
|
|
2692
2932
|
return;
|
|
2693
2933
|
}
|
|
2694
2934
|
const responseEngine = buildResponseEngine2(options);
|
|
2695
|
-
if (!responseEngine)
|
|
2696
|
-
return;
|
|
2935
|
+
if (!responseEngine) return;
|
|
2697
2936
|
const testCase = await createTestCaseDefinition({
|
|
2698
2937
|
name: input.name,
|
|
2699
2938
|
user_prompt: input.user_prompt,
|
|
2700
2939
|
scenario: input.scenario,
|
|
2701
2940
|
metrics: input.metrics,
|
|
2702
|
-
response_engine: responseEngine
|
|
2941
|
+
response_engine: responseEngine,
|
|
2942
|
+
dynamic_variables: input.dynamic_variables,
|
|
2943
|
+
tool_mocks: input.tool_mocks,
|
|
2944
|
+
llm_model: input.llm_model
|
|
2703
2945
|
});
|
|
2704
2946
|
const output = {
|
|
2705
2947
|
message: "Test case definition created successfully",
|
|
@@ -2723,7 +2965,10 @@ var import_fs10 = require("fs");
|
|
|
2723
2965
|
async function updateTestCaseCommand(testCaseDefinitionId, options) {
|
|
2724
2966
|
try {
|
|
2725
2967
|
if (!(0, import_fs10.existsSync)(options.file)) {
|
|
2726
|
-
outputError(
|
|
2968
|
+
outputError(
|
|
2969
|
+
`Test case file not found: ${options.file}`,
|
|
2970
|
+
"FILE_NOT_FOUND"
|
|
2971
|
+
);
|
|
2727
2972
|
return;
|
|
2728
2973
|
}
|
|
2729
2974
|
let input;
|
|
@@ -2732,9 +2977,15 @@ async function updateTestCaseCommand(testCaseDefinitionId, options) {
|
|
|
2732
2977
|
input = JSON.parse(content);
|
|
2733
2978
|
} catch (error) {
|
|
2734
2979
|
if (error instanceof SyntaxError) {
|
|
2735
|
-
outputError(
|
|
2980
|
+
outputError(
|
|
2981
|
+
`Invalid JSON in test case file: ${error.message}`,
|
|
2982
|
+
"INVALID_JSON"
|
|
2983
|
+
);
|
|
2736
2984
|
} else {
|
|
2737
|
-
outputError(
|
|
2985
|
+
outputError(
|
|
2986
|
+
`Error reading test case file: ${error.message}`,
|
|
2987
|
+
"FILE_READ_ERROR"
|
|
2988
|
+
);
|
|
2738
2989
|
}
|
|
2739
2990
|
return;
|
|
2740
2991
|
}
|
|
@@ -2742,7 +2993,10 @@ async function updateTestCaseCommand(testCaseDefinitionId, options) {
|
|
|
2742
2993
|
name: input.name,
|
|
2743
2994
|
user_prompt: input.user_prompt,
|
|
2744
2995
|
scenario: input.scenario,
|
|
2745
|
-
metrics: input.metrics
|
|
2996
|
+
metrics: input.metrics,
|
|
2997
|
+
dynamic_variables: input.dynamic_variables,
|
|
2998
|
+
tool_mocks: input.tool_mocks,
|
|
2999
|
+
llm_model: input.llm_model
|
|
2746
3000
|
});
|
|
2747
3001
|
const output = {
|
|
2748
3002
|
message: "Test case definition updated successfully",
|
|
@@ -2779,13 +3033,19 @@ async function deleteTestCaseCommand(testCaseDefinitionId) {
|
|
|
2779
3033
|
function buildResponseEngine3(options) {
|
|
2780
3034
|
if (options.type === "retell-llm") {
|
|
2781
3035
|
if (!options.llmId) {
|
|
2782
|
-
outputError(
|
|
3036
|
+
outputError(
|
|
3037
|
+
"--llm-id is required when type is retell-llm",
|
|
3038
|
+
"MISSING_PARAMETER"
|
|
3039
|
+
);
|
|
2783
3040
|
return null;
|
|
2784
3041
|
}
|
|
2785
3042
|
return { type: "retell-llm", llm_id: options.llmId };
|
|
2786
3043
|
} else {
|
|
2787
3044
|
if (!options.flowId) {
|
|
2788
|
-
outputError(
|
|
3045
|
+
outputError(
|
|
3046
|
+
"--flow-id is required when type is conversation-flow",
|
|
3047
|
+
"MISSING_PARAMETER"
|
|
3048
|
+
);
|
|
2789
3049
|
return null;
|
|
2790
3050
|
}
|
|
2791
3051
|
return { type: "conversation-flow", conversation_flow_id: options.flowId };
|
|
@@ -2794,8 +3054,7 @@ function buildResponseEngine3(options) {
|
|
|
2794
3054
|
async function listBatchTestsCommand(options) {
|
|
2795
3055
|
try {
|
|
2796
3056
|
const responseEngine = buildResponseEngine3(options);
|
|
2797
|
-
if (!responseEngine)
|
|
2798
|
-
return;
|
|
3057
|
+
if (!responseEngine) return;
|
|
2799
3058
|
const batchTests = await listBatchTests(responseEngine);
|
|
2800
3059
|
const output = {
|
|
2801
3060
|
response_engine: responseEngine,
|
|
@@ -2803,7 +3062,10 @@ async function listBatchTestsCommand(options) {
|
|
|
2803
3062
|
total_count: (batchTests || []).length
|
|
2804
3063
|
};
|
|
2805
3064
|
if (options.fields) {
|
|
2806
|
-
const filtered = filterFields(
|
|
3065
|
+
const filtered = filterFields(
|
|
3066
|
+
output,
|
|
3067
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3068
|
+
);
|
|
2807
3069
|
outputJson(filtered);
|
|
2808
3070
|
} else {
|
|
2809
3071
|
outputJson(output);
|
|
@@ -2818,7 +3080,10 @@ async function getBatchTestCommand(batchJobId, options) {
|
|
|
2818
3080
|
try {
|
|
2819
3081
|
const batchTest = await getBatchTest(batchJobId);
|
|
2820
3082
|
if (options.fields) {
|
|
2821
|
-
const filtered = filterFields(
|
|
3083
|
+
const filtered = filterFields(
|
|
3084
|
+
batchTest,
|
|
3085
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3086
|
+
);
|
|
2822
3087
|
outputJson(filtered);
|
|
2823
3088
|
} else {
|
|
2824
3089
|
outputJson(batchTest);
|
|
@@ -2831,21 +3096,33 @@ async function getBatchTestCommand(batchJobId, options) {
|
|
|
2831
3096
|
// src/commands/tests/batch/create.ts
|
|
2832
3097
|
function buildResponseEngine4(options) {
|
|
2833
3098
|
if (options.llmId && options.flowId) {
|
|
2834
|
-
outputError(
|
|
3099
|
+
outputError(
|
|
3100
|
+
"Cannot specify both --llm-id and --flow-id",
|
|
3101
|
+
"INVALID_PARAMETERS"
|
|
3102
|
+
);
|
|
2835
3103
|
return null;
|
|
2836
3104
|
}
|
|
2837
3105
|
if (!options.llmId && !options.flowId) {
|
|
2838
|
-
outputError(
|
|
3106
|
+
outputError(
|
|
3107
|
+
"Either --llm-id or --flow-id is required",
|
|
3108
|
+
"MISSING_PARAMETER"
|
|
3109
|
+
);
|
|
2839
3110
|
return null;
|
|
2840
3111
|
}
|
|
2841
3112
|
if (options.llmId) {
|
|
2842
|
-
const engine = {
|
|
3113
|
+
const engine = {
|
|
3114
|
+
type: "retell-llm",
|
|
3115
|
+
llm_id: options.llmId
|
|
3116
|
+
};
|
|
2843
3117
|
if (options.version !== void 0) {
|
|
2844
3118
|
engine.version = options.version;
|
|
2845
3119
|
}
|
|
2846
3120
|
return engine;
|
|
2847
3121
|
} else {
|
|
2848
|
-
const engine = {
|
|
3122
|
+
const engine = {
|
|
3123
|
+
type: "conversation-flow",
|
|
3124
|
+
conversation_flow_id: options.flowId
|
|
3125
|
+
};
|
|
2849
3126
|
if (options.version !== void 0) {
|
|
2850
3127
|
engine.version = options.version;
|
|
2851
3128
|
}
|
|
@@ -2855,11 +3132,13 @@ function buildResponseEngine4(options) {
|
|
|
2855
3132
|
async function createBatchTestCommand(options) {
|
|
2856
3133
|
try {
|
|
2857
3134
|
const responseEngine = buildResponseEngine4(options);
|
|
2858
|
-
if (!responseEngine)
|
|
2859
|
-
return;
|
|
3135
|
+
if (!responseEngine) return;
|
|
2860
3136
|
const testCaseDefinitionIds = options.cases.split(",").map((id) => id.trim()).filter(Boolean);
|
|
2861
3137
|
if (testCaseDefinitionIds.length === 0) {
|
|
2862
|
-
outputError(
|
|
3138
|
+
outputError(
|
|
3139
|
+
"At least one test case definition ID is required",
|
|
3140
|
+
"MISSING_PARAMETER"
|
|
3141
|
+
);
|
|
2863
3142
|
return;
|
|
2864
3143
|
}
|
|
2865
3144
|
const batchTest = await createBatchTest({
|
|
@@ -2889,7 +3168,10 @@ async function listTestRunsCommand(batchJobId, options) {
|
|
|
2889
3168
|
total_count: (testRuns || []).length
|
|
2890
3169
|
};
|
|
2891
3170
|
if (options.fields) {
|
|
2892
|
-
const filtered = filterFields(
|
|
3171
|
+
const filtered = filterFields(
|
|
3172
|
+
output,
|
|
3173
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3174
|
+
);
|
|
2893
3175
|
outputJson(filtered);
|
|
2894
3176
|
} else {
|
|
2895
3177
|
outputJson(output);
|
|
@@ -2904,7 +3186,10 @@ async function getTestRunCommand(testRunId, options) {
|
|
|
2904
3186
|
try {
|
|
2905
3187
|
const testRun = await getTestRun(testRunId);
|
|
2906
3188
|
if (options.fields) {
|
|
2907
|
-
const filtered = filterFields(
|
|
3189
|
+
const filtered = filterFields(
|
|
3190
|
+
testRun,
|
|
3191
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3192
|
+
);
|
|
2908
3193
|
outputJson(filtered);
|
|
2909
3194
|
} else {
|
|
2910
3195
|
outputJson(testRun);
|
|
@@ -2919,7 +3204,10 @@ async function listKnowledgeBasesCommand(options) {
|
|
|
2919
3204
|
try {
|
|
2920
3205
|
const client = getRetellClient();
|
|
2921
3206
|
const knowledgeBases = await client.knowledgeBase.list();
|
|
2922
|
-
const output = options.fields ? filterFields(
|
|
3207
|
+
const output = options.fields ? filterFields(
|
|
3208
|
+
knowledgeBases,
|
|
3209
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3210
|
+
) : knowledgeBases;
|
|
2923
3211
|
outputJson(output);
|
|
2924
3212
|
} catch (error) {
|
|
2925
3213
|
handleSdkError(error);
|
|
@@ -2931,7 +3219,10 @@ async function getKnowledgeBaseCommand(knowledgeBaseId, options) {
|
|
|
2931
3219
|
try {
|
|
2932
3220
|
const client = getRetellClient();
|
|
2933
3221
|
const knowledgeBase = await client.knowledgeBase.retrieve(knowledgeBaseId);
|
|
2934
|
-
const output = options.fields ? filterFields(
|
|
3222
|
+
const output = options.fields ? filterFields(
|
|
3223
|
+
knowledgeBase,
|
|
3224
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3225
|
+
) : knowledgeBase;
|
|
2935
3226
|
outputJson(output);
|
|
2936
3227
|
} catch (error) {
|
|
2937
3228
|
handleSdkError(error);
|
|
@@ -2943,7 +3234,10 @@ var import_fs11 = require("fs");
|
|
|
2943
3234
|
async function createKnowledgeBaseCommand(options) {
|
|
2944
3235
|
try {
|
|
2945
3236
|
if (options.name.length > 40) {
|
|
2946
|
-
outputError(
|
|
3237
|
+
outputError(
|
|
3238
|
+
"Knowledge base name must be 40 characters or less",
|
|
3239
|
+
"INVALID_NAME"
|
|
3240
|
+
);
|
|
2947
3241
|
return;
|
|
2948
3242
|
}
|
|
2949
3243
|
const createParams = {
|
|
@@ -2967,27 +3261,40 @@ async function createKnowledgeBaseCommand(options) {
|
|
|
2967
3261
|
const content = (0, import_fs11.readFileSync)(options.texts, "utf-8");
|
|
2968
3262
|
const textsData = JSON.parse(content);
|
|
2969
3263
|
if (!Array.isArray(textsData)) {
|
|
2970
|
-
outputError(
|
|
3264
|
+
outputError(
|
|
3265
|
+
"Texts file must contain an array of { title, text } objects",
|
|
3266
|
+
"INVALID_TEXTS"
|
|
3267
|
+
);
|
|
2971
3268
|
return;
|
|
2972
3269
|
}
|
|
2973
|
-
const texts = textsData.map(
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
3270
|
+
const texts = textsData.map(
|
|
3271
|
+
(entry, index) => {
|
|
3272
|
+
if (typeof entry !== "object" || entry === null) {
|
|
3273
|
+
throw new Error(`Entry at index ${index} must be an object`);
|
|
3274
|
+
}
|
|
3275
|
+
const e = entry;
|
|
3276
|
+
if (typeof e.title !== "string" || typeof e.text !== "string") {
|
|
3277
|
+
throw new Error(
|
|
3278
|
+
`Entry at index ${index} must have "title" and "text" string fields`
|
|
3279
|
+
);
|
|
3280
|
+
}
|
|
3281
|
+
return { title: e.title, text: e.text };
|
|
2980
3282
|
}
|
|
2981
|
-
|
|
2982
|
-
});
|
|
3283
|
+
);
|
|
2983
3284
|
if (texts.length > 0) {
|
|
2984
3285
|
createParams.knowledge_base_texts = texts;
|
|
2985
3286
|
}
|
|
2986
3287
|
} catch (error) {
|
|
2987
3288
|
if (error instanceof SyntaxError) {
|
|
2988
|
-
outputError(
|
|
3289
|
+
outputError(
|
|
3290
|
+
`Invalid JSON in texts file: ${error.message}`,
|
|
3291
|
+
"INVALID_JSON"
|
|
3292
|
+
);
|
|
2989
3293
|
} else if (error instanceof Error) {
|
|
2990
|
-
outputError(
|
|
3294
|
+
outputError(
|
|
3295
|
+
`Error parsing texts file: ${error.message}`,
|
|
3296
|
+
"INVALID_TEXTS"
|
|
3297
|
+
);
|
|
2991
3298
|
}
|
|
2992
3299
|
return;
|
|
2993
3300
|
}
|
|
@@ -3031,7 +3338,10 @@ var import_fs12 = require("fs");
|
|
|
3031
3338
|
async function addKnowledgeBaseSourcesCommand(knowledgeBaseId, options) {
|
|
3032
3339
|
try {
|
|
3033
3340
|
if (!options.urls && !options.texts) {
|
|
3034
|
-
outputError(
|
|
3341
|
+
outputError(
|
|
3342
|
+
"At least one of --urls or --texts must be provided",
|
|
3343
|
+
"MISSING_SOURCES"
|
|
3344
|
+
);
|
|
3035
3345
|
return;
|
|
3036
3346
|
}
|
|
3037
3347
|
const addParams = {};
|
|
@@ -3050,33 +3360,49 @@ async function addKnowledgeBaseSourcesCommand(knowledgeBaseId, options) {
|
|
|
3050
3360
|
const content = (0, import_fs12.readFileSync)(options.texts, "utf-8");
|
|
3051
3361
|
const textsData = JSON.parse(content);
|
|
3052
3362
|
if (!Array.isArray(textsData)) {
|
|
3053
|
-
outputError(
|
|
3363
|
+
outputError(
|
|
3364
|
+
"Texts file must contain an array of { title, text } objects",
|
|
3365
|
+
"INVALID_TEXTS"
|
|
3366
|
+
);
|
|
3054
3367
|
return;
|
|
3055
3368
|
}
|
|
3056
|
-
const texts = textsData.map(
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3369
|
+
const texts = textsData.map(
|
|
3370
|
+
(entry, index) => {
|
|
3371
|
+
if (typeof entry !== "object" || entry === null) {
|
|
3372
|
+
throw new Error(`Entry at index ${index} must be an object`);
|
|
3373
|
+
}
|
|
3374
|
+
const e = entry;
|
|
3375
|
+
if (typeof e.title !== "string" || typeof e.text !== "string") {
|
|
3376
|
+
throw new Error(
|
|
3377
|
+
`Entry at index ${index} must have "title" and "text" string fields`
|
|
3378
|
+
);
|
|
3379
|
+
}
|
|
3380
|
+
return { title: e.title, text: e.text };
|
|
3063
3381
|
}
|
|
3064
|
-
|
|
3065
|
-
});
|
|
3382
|
+
);
|
|
3066
3383
|
if (texts.length > 0) {
|
|
3067
3384
|
addParams.knowledge_base_texts = texts;
|
|
3068
3385
|
}
|
|
3069
3386
|
} catch (error) {
|
|
3070
3387
|
if (error instanceof SyntaxError) {
|
|
3071
|
-
outputError(
|
|
3388
|
+
outputError(
|
|
3389
|
+
`Invalid JSON in texts file: ${error.message}`,
|
|
3390
|
+
"INVALID_JSON"
|
|
3391
|
+
);
|
|
3072
3392
|
} else if (error instanceof Error) {
|
|
3073
|
-
outputError(
|
|
3393
|
+
outputError(
|
|
3394
|
+
`Error parsing texts file: ${error.message}`,
|
|
3395
|
+
"INVALID_TEXTS"
|
|
3396
|
+
);
|
|
3074
3397
|
}
|
|
3075
3398
|
return;
|
|
3076
3399
|
}
|
|
3077
3400
|
}
|
|
3078
3401
|
const client = getRetellClient();
|
|
3079
|
-
const knowledgeBase = await client.knowledgeBase.addSources(
|
|
3402
|
+
const knowledgeBase = await client.knowledgeBase.addSources(
|
|
3403
|
+
knowledgeBaseId,
|
|
3404
|
+
addParams
|
|
3405
|
+
);
|
|
3080
3406
|
const output = {
|
|
3081
3407
|
message: "Sources added successfully",
|
|
3082
3408
|
knowledge_base_id: knowledgeBase.knowledge_base_id,
|
|
@@ -3097,7 +3423,10 @@ async function addKnowledgeBaseSourcesCommand(knowledgeBaseId, options) {
|
|
|
3097
3423
|
async function deleteKnowledgeBaseSourceCommand(knowledgeBaseId, sourceId) {
|
|
3098
3424
|
try {
|
|
3099
3425
|
const client = getRetellClient();
|
|
3100
|
-
const knowledgeBase = await client.knowledgeBase.deleteSource(
|
|
3426
|
+
const knowledgeBase = await client.knowledgeBase.deleteSource(
|
|
3427
|
+
knowledgeBaseId,
|
|
3428
|
+
sourceId
|
|
3429
|
+
);
|
|
3101
3430
|
const output = {
|
|
3102
3431
|
message: "Source deleted successfully",
|
|
3103
3432
|
knowledge_base_id: knowledgeBase.knowledge_base_id,
|
|
@@ -3121,7 +3450,10 @@ async function listFlowsCommand(options) {
|
|
|
3121
3450
|
const flows2 = await client.conversationFlow.list({
|
|
3122
3451
|
limit: options.limit || 100
|
|
3123
3452
|
});
|
|
3124
|
-
const output = options.fields ? filterFields(
|
|
3453
|
+
const output = options.fields ? filterFields(
|
|
3454
|
+
flows2,
|
|
3455
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3456
|
+
) : flows2;
|
|
3125
3457
|
outputJson(output);
|
|
3126
3458
|
} catch (error) {
|
|
3127
3459
|
handleSdkError(error);
|
|
@@ -3136,8 +3468,14 @@ async function getFlowCommand(conversationFlowId, options) {
|
|
|
3136
3468
|
if (options.version !== void 0) {
|
|
3137
3469
|
retrieveOptions.version = options.version;
|
|
3138
3470
|
}
|
|
3139
|
-
const flow = await client.conversationFlow.retrieve(
|
|
3140
|
-
|
|
3471
|
+
const flow = await client.conversationFlow.retrieve(
|
|
3472
|
+
conversationFlowId,
|
|
3473
|
+
retrieveOptions
|
|
3474
|
+
);
|
|
3475
|
+
const output = options.fields ? filterFields(
|
|
3476
|
+
flow,
|
|
3477
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3478
|
+
) : flow;
|
|
3141
3479
|
outputJson(output);
|
|
3142
3480
|
} catch (error) {
|
|
3143
3481
|
handleSdkError(error);
|
|
@@ -3158,14 +3496,23 @@ async function createFlowCommand(options) {
|
|
|
3158
3496
|
flowConfig = JSON.parse(content);
|
|
3159
3497
|
} catch (error) {
|
|
3160
3498
|
if (error instanceof SyntaxError) {
|
|
3161
|
-
outputError(
|
|
3499
|
+
outputError(
|
|
3500
|
+
`Invalid JSON in flow file: ${error.message}`,
|
|
3501
|
+
"INVALID_JSON"
|
|
3502
|
+
);
|
|
3162
3503
|
} else if (error instanceof Error) {
|
|
3163
|
-
outputError(
|
|
3504
|
+
outputError(
|
|
3505
|
+
`Error reading flow file: ${error.message}`,
|
|
3506
|
+
"FILE_READ_ERROR"
|
|
3507
|
+
);
|
|
3164
3508
|
}
|
|
3165
3509
|
return;
|
|
3166
3510
|
}
|
|
3167
3511
|
if (!flowConfig.start_speaker) {
|
|
3168
|
-
outputError(
|
|
3512
|
+
outputError(
|
|
3513
|
+
'Flow configuration must have a "start_speaker" field (user or agent)',
|
|
3514
|
+
"INVALID_FLOW"
|
|
3515
|
+
);
|
|
3169
3516
|
return;
|
|
3170
3517
|
}
|
|
3171
3518
|
const client = getRetellClient();
|
|
@@ -3196,9 +3543,15 @@ async function updateFlowCommand(conversationFlowId, options) {
|
|
|
3196
3543
|
flowUpdates = JSON.parse(content);
|
|
3197
3544
|
} catch (error) {
|
|
3198
3545
|
if (error instanceof SyntaxError) {
|
|
3199
|
-
outputError(
|
|
3546
|
+
outputError(
|
|
3547
|
+
`Invalid JSON in flow file: ${error.message}`,
|
|
3548
|
+
"INVALID_JSON"
|
|
3549
|
+
);
|
|
3200
3550
|
} else if (error instanceof Error) {
|
|
3201
|
-
outputError(
|
|
3551
|
+
outputError(
|
|
3552
|
+
`Error reading flow file: ${error.message}`,
|
|
3553
|
+
"FILE_READ_ERROR"
|
|
3554
|
+
);
|
|
3202
3555
|
}
|
|
3203
3556
|
return;
|
|
3204
3557
|
}
|
|
@@ -3206,7 +3559,10 @@ async function updateFlowCommand(conversationFlowId, options) {
|
|
|
3206
3559
|
flowUpdates.version = options.version;
|
|
3207
3560
|
}
|
|
3208
3561
|
const client = getRetellClient();
|
|
3209
|
-
const flow = await client.conversationFlow.update(
|
|
3562
|
+
const flow = await client.conversationFlow.update(
|
|
3563
|
+
conversationFlowId,
|
|
3564
|
+
flowUpdates
|
|
3565
|
+
);
|
|
3210
3566
|
const output = {
|
|
3211
3567
|
message: "Conversation flow updated successfully",
|
|
3212
3568
|
conversation_flow_id: flow.conversation_flow_id,
|
|
@@ -3241,22 +3597,35 @@ var packageJson = JSON.parse(
|
|
|
3241
3597
|
);
|
|
3242
3598
|
var program = new import_commander.Command();
|
|
3243
3599
|
program.name("retell").description("Retell AI CLI - Manage transcripts and agent prompts").version(packageJson.version, "-v, --version", "Display version number").helpOption("-h, --help", "Display help for command").option("--json", "Output as JSON (default)", true);
|
|
3244
|
-
program.command("login").description("Authenticate with Retell AI").addHelpText(
|
|
3600
|
+
program.command("login").description("Authenticate with Retell AI").addHelpText(
|
|
3601
|
+
"after",
|
|
3602
|
+
`
|
|
3245
3603
|
Examples:
|
|
3246
3604
|
$ retell login
|
|
3247
3605
|
# Enter your API key when prompted
|
|
3248
3606
|
# Creates .retellrc.json in current directory
|
|
3249
|
-
`
|
|
3607
|
+
`
|
|
3608
|
+
).action(async () => {
|
|
3250
3609
|
await loginCommand();
|
|
3251
3610
|
});
|
|
3252
3611
|
var transcripts = program.command("transcripts").description("Manage call transcripts");
|
|
3253
|
-
transcripts.command("list").description("List all call transcripts").option(
|
|
3612
|
+
transcripts.command("list").description("List all call transcripts").option(
|
|
3613
|
+
"-l, --limit <number>",
|
|
3614
|
+
"Maximum number of calls to return (default: 50)",
|
|
3615
|
+
"50"
|
|
3616
|
+
).option(
|
|
3617
|
+
"--fields <fields>",
|
|
3618
|
+
"Comma-separated list of fields to return (e.g., call_id,call_status,metadata.duration)"
|
|
3619
|
+
).addHelpText(
|
|
3620
|
+
"after",
|
|
3621
|
+
`
|
|
3254
3622
|
Examples:
|
|
3255
3623
|
$ retell transcripts list
|
|
3256
3624
|
$ retell transcripts list --limit 100
|
|
3257
3625
|
$ retell transcripts list --fields call_id,call_status
|
|
3258
3626
|
$ retell transcripts list | jq '.[] | select(.call_status == "error")'
|
|
3259
|
-
`
|
|
3627
|
+
`
|
|
3628
|
+
).action(async (options) => {
|
|
3260
3629
|
const limit = parseInt(options.limit, 10);
|
|
3261
3630
|
if (isNaN(limit) || limit < 1) {
|
|
3262
3631
|
console.error("Error: limit must be a positive number");
|
|
@@ -3267,17 +3636,44 @@ Examples:
|
|
|
3267
3636
|
fields: options.fields
|
|
3268
3637
|
});
|
|
3269
3638
|
});
|
|
3270
|
-
transcripts.command("get <call_id>").description("Get a specific call transcript").option(
|
|
3639
|
+
transcripts.command("get <call_id>").description("Get a specific call transcript").option(
|
|
3640
|
+
"--fields <fields>",
|
|
3641
|
+
"Comma-separated list of fields to return (e.g., call_id,metadata.duration,analysis)"
|
|
3642
|
+
).addHelpText(
|
|
3643
|
+
"after",
|
|
3644
|
+
`
|
|
3271
3645
|
Examples:
|
|
3272
3646
|
$ retell transcripts get call_abc123
|
|
3273
3647
|
$ retell transcripts get call_abc123 --fields call_id,metadata.duration
|
|
3274
3648
|
$ retell transcripts get call_abc123 | jq '.transcript_object'
|
|
3275
|
-
`
|
|
3649
|
+
`
|
|
3650
|
+
).action(async (callId, options) => {
|
|
3276
3651
|
await getTranscriptCommand(callId, {
|
|
3277
3652
|
fields: options.fields
|
|
3278
3653
|
});
|
|
3279
3654
|
});
|
|
3280
|
-
transcripts.command("analyze <call_id>").description(
|
|
3655
|
+
transcripts.command("analyze <call_id>").description(
|
|
3656
|
+
"Analyze a call transcript with performance metrics and insights"
|
|
3657
|
+
).option(
|
|
3658
|
+
"--fields <fields>",
|
|
3659
|
+
"Comma-separated list of fields to return (e.g., call_id,performance,analysis.summary)"
|
|
3660
|
+
).option(
|
|
3661
|
+
"--raw",
|
|
3662
|
+
"Return unmodified API response instead of enriched analysis"
|
|
3663
|
+
).option(
|
|
3664
|
+
"--hotspots-only",
|
|
3665
|
+
"Return only conversation hotspots/issues for troubleshooting"
|
|
3666
|
+
).option(
|
|
3667
|
+
"--latency-threshold <ms>",
|
|
3668
|
+
`Latency threshold in ms for hotspot detection (default: ${DEFAULT_LATENCY_THRESHOLD})`,
|
|
3669
|
+
String(DEFAULT_LATENCY_THRESHOLD)
|
|
3670
|
+
).option(
|
|
3671
|
+
"--silence-threshold <ms>",
|
|
3672
|
+
`Silence threshold in ms for hotspot detection (default: ${DEFAULT_SILENCE_THRESHOLD})`,
|
|
3673
|
+
String(DEFAULT_SILENCE_THRESHOLD)
|
|
3674
|
+
).addHelpText(
|
|
3675
|
+
"after",
|
|
3676
|
+
`
|
|
3281
3677
|
Examples:
|
|
3282
3678
|
$ retell transcripts analyze call_abc123
|
|
3283
3679
|
$ retell transcripts analyze call_abc123 --fields call_id,performance
|
|
@@ -3287,7 +3683,8 @@ Examples:
|
|
|
3287
3683
|
$ retell transcripts analyze call_abc123 --hotspots-only --latency-threshold 1500
|
|
3288
3684
|
$ retell transcripts analyze call_abc123 --hotspots-only --fields hotspots
|
|
3289
3685
|
$ retell transcripts analyze call_abc123 | jq '.performance.latency_p50_ms'
|
|
3290
|
-
`
|
|
3686
|
+
`
|
|
3687
|
+
).action(async (callId, options) => {
|
|
3291
3688
|
await analyzeTranscriptCommand(callId, {
|
|
3292
3689
|
fields: options.fields,
|
|
3293
3690
|
raw: options.raw,
|
|
@@ -3296,14 +3693,23 @@ Examples:
|
|
|
3296
3693
|
silenceThreshold: options.silenceThreshold ? parseInt(options.silenceThreshold) : void 0
|
|
3297
3694
|
});
|
|
3298
3695
|
});
|
|
3299
|
-
transcripts.command("search").description("Search transcripts with advanced filtering").option("--status <status>", "Filter by call status (error, ended, ongoing)").option("--agent-id <id>", "Filter by agent ID").option(
|
|
3696
|
+
transcripts.command("search").description("Search transcripts with advanced filtering").option("--status <status>", "Filter by call status (error, ended, ongoing)").option("--agent-id <id>", "Filter by agent ID").option(
|
|
3697
|
+
"--since <date>",
|
|
3698
|
+
"Filter calls after this date (YYYY-MM-DD or ISO format)"
|
|
3699
|
+
).option(
|
|
3700
|
+
"--until <date>",
|
|
3701
|
+
"Filter calls before this date (YYYY-MM-DD or ISO format)"
|
|
3702
|
+
).option("--limit <number>", "Maximum number of results (default: 50)", "50").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
3703
|
+
"after",
|
|
3704
|
+
`
|
|
3300
3705
|
Examples:
|
|
3301
3706
|
$ retell transcripts search --status error
|
|
3302
3707
|
$ retell transcripts search --agent-id agent_123 --since 2025-11-01
|
|
3303
3708
|
$ retell transcripts search --status error --limit 10
|
|
3304
3709
|
$ retell transcripts search --status error --fields call_id,agent_id,call_status
|
|
3305
3710
|
$ retell transcripts search --since 2025-11-01 --until 2025-11-15
|
|
3306
|
-
`
|
|
3711
|
+
`
|
|
3712
|
+
).action(async (options) => {
|
|
3307
3713
|
await searchTranscriptsCommand({
|
|
3308
3714
|
status: options.status,
|
|
3309
3715
|
agentId: options.agentId,
|
|
@@ -3314,13 +3720,23 @@ Examples:
|
|
|
3314
3720
|
});
|
|
3315
3721
|
});
|
|
3316
3722
|
var agents = program.command("agents").description("Manage agents");
|
|
3317
|
-
agents.command("list").description("List all agents").option(
|
|
3723
|
+
agents.command("list").description("List all agents").option(
|
|
3724
|
+
"-l, --limit <number>",
|
|
3725
|
+
"Maximum number of agents to return (default: 100)",
|
|
3726
|
+
"100"
|
|
3727
|
+
).option(
|
|
3728
|
+
"--fields <fields>",
|
|
3729
|
+
"Comma-separated list of fields to return (e.g., agent_id,agent_name,response_engine_type)"
|
|
3730
|
+
).addHelpText(
|
|
3731
|
+
"after",
|
|
3732
|
+
`
|
|
3318
3733
|
Examples:
|
|
3319
3734
|
$ retell agents list
|
|
3320
3735
|
$ retell agents list --limit 10
|
|
3321
3736
|
$ retell agents list --fields agent_id,agent_name
|
|
3322
3737
|
$ retell agents list | jq '.[] | select(.response_engine.type == "retell-llm")'
|
|
3323
|
-
`
|
|
3738
|
+
`
|
|
3739
|
+
).action(async (options) => {
|
|
3324
3740
|
const limit = parseInt(options.limit, 10);
|
|
3325
3741
|
if (isNaN(limit) || limit < 1) {
|
|
3326
3742
|
console.error("Error: limit must be a positive number");
|
|
@@ -3331,78 +3747,126 @@ Examples:
|
|
|
3331
3747
|
fields: options.fields
|
|
3332
3748
|
});
|
|
3333
3749
|
});
|
|
3334
|
-
agents.command("info <agent_id>").description("Get detailed agent information").option(
|
|
3750
|
+
agents.command("info <agent_id>").description("Get detailed agent information").option(
|
|
3751
|
+
"--fields <fields>",
|
|
3752
|
+
"Comma-separated list of fields to return (e.g., agent_name,response_engine.type,voice_config)"
|
|
3753
|
+
).addHelpText(
|
|
3754
|
+
"after",
|
|
3755
|
+
`
|
|
3335
3756
|
Examples:
|
|
3336
3757
|
$ retell agents info agent_123abc
|
|
3337
3758
|
$ retell agents info agent_123abc --fields agent_name,response_engine.type
|
|
3338
3759
|
$ retell agents info agent_123abc | jq '.response_engine.type'
|
|
3339
|
-
`
|
|
3760
|
+
`
|
|
3761
|
+
).action(async (agentId, options) => {
|
|
3340
3762
|
await agentInfoCommand(agentId, {
|
|
3341
3763
|
fields: options.fields
|
|
3342
3764
|
});
|
|
3343
3765
|
});
|
|
3344
3766
|
var prompts = program.command("prompts").description("Manage agent prompts");
|
|
3345
|
-
prompts.command("pull <agent_id>").description("Download agent prompts to a local file").option(
|
|
3767
|
+
prompts.command("pull <agent_id>").description("Download agent prompts to a local file").option(
|
|
3768
|
+
"-o, --output <path>",
|
|
3769
|
+
"Output file path (default: .retell-prompts/<agent_id>.json)",
|
|
3770
|
+
".retell-prompts"
|
|
3771
|
+
).addHelpText(
|
|
3772
|
+
"after",
|
|
3773
|
+
`
|
|
3346
3774
|
Examples:
|
|
3347
3775
|
$ retell prompts pull agent_123abc
|
|
3348
3776
|
$ retell prompts pull agent_123abc --output my-prompts.json
|
|
3349
|
-
`
|
|
3777
|
+
`
|
|
3778
|
+
).action(async (agentId, options) => {
|
|
3350
3779
|
await pullPromptsCommand(agentId, options);
|
|
3351
3780
|
});
|
|
3352
|
-
prompts.command("diff <agent_id>").description("Show differences between local and remote prompts").option(
|
|
3781
|
+
prompts.command("diff <agent_id>").description("Show differences between local and remote prompts").option(
|
|
3782
|
+
"-s, --source <path>",
|
|
3783
|
+
"Source directory path (default: .retell-prompts)",
|
|
3784
|
+
".retell-prompts"
|
|
3785
|
+
).option("-f, --fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
3786
|
+
"after",
|
|
3787
|
+
`
|
|
3353
3788
|
Examples:
|
|
3354
3789
|
$ retell prompts diff agent_123abc
|
|
3355
3790
|
$ retell prompts diff agent_123abc --source ./custom-prompts
|
|
3356
3791
|
$ retell prompts diff agent_123abc --fields has_changes,changes.general_prompt
|
|
3357
|
-
`
|
|
3792
|
+
`
|
|
3793
|
+
).action(async (agentId, options) => {
|
|
3358
3794
|
await diffPromptsCommand(agentId, options);
|
|
3359
3795
|
});
|
|
3360
|
-
prompts.command("update <agent_id>").description("Update agent prompts from a local file").option(
|
|
3796
|
+
prompts.command("update <agent_id>").description("Update agent prompts from a local file").option(
|
|
3797
|
+
"-s, --source <path>",
|
|
3798
|
+
"Source file path (default: .retell-prompts/<agent_id>.json)",
|
|
3799
|
+
".retell-prompts"
|
|
3800
|
+
).option("--dry-run", "Preview changes without applying them", false).addHelpText(
|
|
3801
|
+
"after",
|
|
3802
|
+
`
|
|
3361
3803
|
Examples:
|
|
3362
3804
|
$ retell prompts update agent_123abc --source my-prompts.json --dry-run
|
|
3363
3805
|
$ retell prompts update agent_123abc --source my-prompts.json
|
|
3364
3806
|
# Remember to publish: retell agent-publish agent_123abc
|
|
3365
|
-
`
|
|
3807
|
+
`
|
|
3808
|
+
).action(async (agentId, options) => {
|
|
3366
3809
|
await updatePromptsCommand(agentId, options);
|
|
3367
3810
|
});
|
|
3368
|
-
program.command("agent-publish <agent_id>").description("Publish a draft agent to make changes live").addHelpText(
|
|
3811
|
+
program.command("agent-publish <agent_id>").description("Publish a draft agent to make changes live").addHelpText(
|
|
3812
|
+
"after",
|
|
3813
|
+
`
|
|
3369
3814
|
Examples:
|
|
3370
3815
|
$ retell agent-publish agent_123abc
|
|
3371
3816
|
# Run this after updating prompts to make changes live
|
|
3372
|
-
`
|
|
3817
|
+
`
|
|
3818
|
+
).action(async (agentId) => {
|
|
3373
3819
|
await publishAgentCommand(agentId);
|
|
3374
3820
|
});
|
|
3375
3821
|
var tools = program.command("tools").description("Manage agent tools (custom functions, webhooks, etc.)");
|
|
3376
|
-
tools.command("list <agent_id>").description("List all tools configured for an agent").option("--state <name>", "Filter by state name (Retell LLM only)").option("--component <id>", "Filter by component ID (Conversation Flow only)").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
3822
|
+
tools.command("list <agent_id>").description("List all tools configured for an agent").option("--state <name>", "Filter by state name (Retell LLM only)").option("--component <id>", "Filter by component ID (Conversation Flow only)").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
3823
|
+
"after",
|
|
3824
|
+
`
|
|
3377
3825
|
Examples:
|
|
3378
3826
|
$ retell tools list agent_123abc
|
|
3379
3827
|
$ retell tools list agent_123abc --state greeting
|
|
3380
3828
|
$ retell tools list agent_123abc --fields total_count,general_tools
|
|
3381
|
-
`
|
|
3829
|
+
`
|
|
3830
|
+
).action(async (agentId, options) => {
|
|
3382
3831
|
await listToolsCommand(agentId, {
|
|
3383
3832
|
state: options.state,
|
|
3384
3833
|
component: options.component,
|
|
3385
3834
|
fields: options.fields
|
|
3386
3835
|
});
|
|
3387
3836
|
});
|
|
3388
|
-
tools.command("get <agent_id> <tool_name>").description("Get detailed information about a specific tool").option("--state <name>", "State name to search within (Retell LLM only)").option(
|
|
3837
|
+
tools.command("get <agent_id> <tool_name>").description("Get detailed information about a specific tool").option("--state <name>", "State name to search within (Retell LLM only)").option(
|
|
3838
|
+
"--component <id>",
|
|
3839
|
+
"Component ID to search within (Conversation Flow only)"
|
|
3840
|
+
).option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
3841
|
+
"after",
|
|
3842
|
+
`
|
|
3389
3843
|
Examples:
|
|
3390
3844
|
$ retell tools get agent_123abc lookup_customer
|
|
3391
3845
|
$ retell tools get agent_123abc book_cal --state booking
|
|
3392
3846
|
$ retell tools get agent_123abc my_tool --fields tool.name,tool.type
|
|
3393
|
-
`
|
|
3847
|
+
`
|
|
3848
|
+
).action(async (agentId, toolName, options) => {
|
|
3394
3849
|
await getToolCommand(agentId, toolName, {
|
|
3395
3850
|
state: options.state,
|
|
3396
3851
|
component: options.component,
|
|
3397
3852
|
fields: options.fields
|
|
3398
3853
|
});
|
|
3399
3854
|
});
|
|
3400
|
-
tools.command("add <agent_id>").description("Add a new tool to an agent").requiredOption(
|
|
3855
|
+
tools.command("add <agent_id>").description("Add a new tool to an agent").requiredOption(
|
|
3856
|
+
"-f, --file <path>",
|
|
3857
|
+
"Path to JSON file containing tool definition"
|
|
3858
|
+
).option("--state <name>", "Add to specific state (Retell LLM only)").option(
|
|
3859
|
+
"--component <id>",
|
|
3860
|
+
"Add to specific component (Conversation Flow only)"
|
|
3861
|
+
).option("--dry-run", "Preview changes without applying them").addHelpText(
|
|
3862
|
+
"after",
|
|
3863
|
+
`
|
|
3401
3864
|
Examples:
|
|
3402
3865
|
$ retell tools add agent_123abc --file tool.json
|
|
3403
3866
|
$ retell tools add agent_123abc --file tool.json --state booking
|
|
3404
3867
|
$ retell tools add agent_123abc --file tool.json --dry-run
|
|
3405
|
-
`
|
|
3868
|
+
`
|
|
3869
|
+
).action(async (agentId, options) => {
|
|
3406
3870
|
await addToolCommand(agentId, {
|
|
3407
3871
|
file: options.file,
|
|
3408
3872
|
state: options.state,
|
|
@@ -3410,12 +3874,21 @@ Examples:
|
|
|
3410
3874
|
dryRun: options.dryRun
|
|
3411
3875
|
});
|
|
3412
3876
|
});
|
|
3413
|
-
tools.command("update <agent_id> <tool_name>").description("Update an existing tool").requiredOption(
|
|
3877
|
+
tools.command("update <agent_id> <tool_name>").description("Update an existing tool").requiredOption(
|
|
3878
|
+
"-f, --file <path>",
|
|
3879
|
+
"Path to JSON file containing updated tool definition"
|
|
3880
|
+
).option("--state <name>", "State where tool exists (Retell LLM only)").option(
|
|
3881
|
+
"--component <id>",
|
|
3882
|
+
"Component where tool exists (Conversation Flow only)"
|
|
3883
|
+
).option("--dry-run", "Preview changes without applying them").addHelpText(
|
|
3884
|
+
"after",
|
|
3885
|
+
`
|
|
3414
3886
|
Examples:
|
|
3415
3887
|
$ retell tools update agent_123abc lookup_customer --file tool.json
|
|
3416
3888
|
$ retell tools update agent_123abc book_cal --file tool.json --state booking
|
|
3417
3889
|
$ retell tools update agent_123abc my_tool --file tool.json --dry-run
|
|
3418
|
-
`
|
|
3890
|
+
`
|
|
3891
|
+
).action(async (agentId, toolName, options) => {
|
|
3419
3892
|
await updateToolCommand(agentId, toolName, {
|
|
3420
3893
|
file: options.file,
|
|
3421
3894
|
state: options.state,
|
|
@@ -3423,34 +3896,55 @@ Examples:
|
|
|
3423
3896
|
dryRun: options.dryRun
|
|
3424
3897
|
});
|
|
3425
3898
|
});
|
|
3426
|
-
tools.command("remove <agent_id> <tool_name>").description("Remove a tool from an agent").option("--state <name>", "State where tool exists (Retell LLM only)").option(
|
|
3899
|
+
tools.command("remove <agent_id> <tool_name>").description("Remove a tool from an agent").option("--state <name>", "State where tool exists (Retell LLM only)").option(
|
|
3900
|
+
"--component <id>",
|
|
3901
|
+
"Component where tool exists (Conversation Flow only)"
|
|
3902
|
+
).option("--dry-run", "Preview changes without applying them").addHelpText(
|
|
3903
|
+
"after",
|
|
3904
|
+
`
|
|
3427
3905
|
Examples:
|
|
3428
3906
|
$ retell tools remove agent_123abc lookup_customer
|
|
3429
3907
|
$ retell tools remove agent_123abc book_cal --state booking
|
|
3430
3908
|
$ retell tools remove agent_123abc my_tool --dry-run
|
|
3431
|
-
`
|
|
3909
|
+
`
|
|
3910
|
+
).action(async (agentId, toolName, options) => {
|
|
3432
3911
|
await removeToolCommand(agentId, toolName, {
|
|
3433
3912
|
state: options.state,
|
|
3434
3913
|
component: options.component,
|
|
3435
3914
|
dryRun: options.dryRun
|
|
3436
3915
|
});
|
|
3437
3916
|
});
|
|
3438
|
-
tools.command("export <agent_id>").description("Export all tools from an agent to a JSON file").option(
|
|
3917
|
+
tools.command("export <agent_id>").description("Export all tools from an agent to a JSON file").option(
|
|
3918
|
+
"-o, --output <path>",
|
|
3919
|
+
"Output file path (prints to stdout if not specified)"
|
|
3920
|
+
).addHelpText(
|
|
3921
|
+
"after",
|
|
3922
|
+
`
|
|
3439
3923
|
Examples:
|
|
3440
3924
|
$ retell tools export agent_123abc
|
|
3441
3925
|
$ retell tools export agent_123abc --output tools.json
|
|
3442
3926
|
$ retell tools export agent_123abc > tools.json
|
|
3443
|
-
`
|
|
3927
|
+
`
|
|
3928
|
+
).action(async (agentId, options) => {
|
|
3444
3929
|
await exportToolsCommand(agentId, {
|
|
3445
3930
|
output: options.output
|
|
3446
3931
|
});
|
|
3447
3932
|
});
|
|
3448
|
-
tools.command("import <agent_id>").description("Import tools from a JSON file to an agent").requiredOption(
|
|
3933
|
+
tools.command("import <agent_id>").description("Import tools from a JSON file to an agent").requiredOption(
|
|
3934
|
+
"-f, --file <path>",
|
|
3935
|
+
"Path to JSON file containing tools to import"
|
|
3936
|
+
).option("--dry-run", "Preview changes without applying them").option(
|
|
3937
|
+
"--replace",
|
|
3938
|
+
"Replace existing tools with same name instead of skipping"
|
|
3939
|
+
).addHelpText(
|
|
3940
|
+
"after",
|
|
3941
|
+
`
|
|
3449
3942
|
Examples:
|
|
3450
3943
|
$ retell tools import agent_123abc --file tools.json
|
|
3451
3944
|
$ retell tools import agent_123abc --file tools.json --dry-run
|
|
3452
3945
|
$ retell tools import agent_123abc --file tools.json --replace
|
|
3453
|
-
`
|
|
3946
|
+
`
|
|
3947
|
+
).action(async (agentId, options) => {
|
|
3454
3948
|
await importToolsCommand(agentId, {
|
|
3455
3949
|
file: options.file,
|
|
3456
3950
|
dryRun: options.dryRun,
|
|
@@ -3459,12 +3953,18 @@ Examples:
|
|
|
3459
3953
|
});
|
|
3460
3954
|
var tests = program.command("tests").description("Manage test cases, batch tests, and test runs");
|
|
3461
3955
|
var testsCases = tests.command("cases").description("Manage test case definitions");
|
|
3462
|
-
testsCases.command("list").description("List all test case definitions for an LLM or flow").requiredOption(
|
|
3956
|
+
testsCases.command("list").description("List all test case definitions for an LLM or flow").requiredOption(
|
|
3957
|
+
"-t, --type <type>",
|
|
3958
|
+
"Response engine type (retell-llm or conversation-flow)"
|
|
3959
|
+
).option("--llm-id <id>", "LLM ID (required when type is retell-llm)").option("--flow-id <id>", "Flow ID (required when type is conversation-flow)").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
3960
|
+
"after",
|
|
3961
|
+
`
|
|
3463
3962
|
Examples:
|
|
3464
3963
|
$ retell tests cases list --type retell-llm --llm-id llm_abc123
|
|
3465
3964
|
$ retell tests cases list --type conversation-flow --flow-id cf_abc123
|
|
3466
3965
|
$ retell tests cases list --type retell-llm --llm-id llm_abc123 --fields test_case_definitions
|
|
3467
|
-
`
|
|
3966
|
+
`
|
|
3967
|
+
).action(async (options) => {
|
|
3468
3968
|
if (options.type !== "retell-llm" && options.type !== "conversation-flow") {
|
|
3469
3969
|
console.error('Error: type must be "retell-llm" or "conversation-flow"');
|
|
3470
3970
|
process.exit(1);
|
|
@@ -3476,16 +3976,24 @@ Examples:
|
|
|
3476
3976
|
fields: options.fields
|
|
3477
3977
|
});
|
|
3478
3978
|
});
|
|
3479
|
-
testsCases.command("get <test_case_definition_id>").description("Get a specific test case definition").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
3979
|
+
testsCases.command("get <test_case_definition_id>").description("Get a specific test case definition").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
3980
|
+
"after",
|
|
3981
|
+
`
|
|
3480
3982
|
Examples:
|
|
3481
3983
|
$ retell tests cases get tcd_abc123
|
|
3482
3984
|
$ retell tests cases get tcd_abc123 --fields name,user_prompt
|
|
3483
|
-
`
|
|
3985
|
+
`
|
|
3986
|
+
).action(async (testCaseDefinitionId, options) => {
|
|
3484
3987
|
await getTestCaseCommand(testCaseDefinitionId, {
|
|
3485
3988
|
fields: options.fields
|
|
3486
3989
|
});
|
|
3487
3990
|
});
|
|
3488
|
-
testsCases.command("create").description("Create a new test case definition from a JSON file").requiredOption(
|
|
3991
|
+
testsCases.command("create").description("Create a new test case definition from a JSON file").requiredOption(
|
|
3992
|
+
"-f, --file <path>",
|
|
3993
|
+
"Path to JSON file containing test case definition"
|
|
3994
|
+
).option("--llm-id <id>", "LLM ID (mutually exclusive with --flow-id)").option("--flow-id <id>", "Flow ID (mutually exclusive with --llm-id)").option("--version <number>", "Version of the LLM or flow (optional)").addHelpText(
|
|
3995
|
+
"after",
|
|
3996
|
+
`
|
|
3489
3997
|
Examples:
|
|
3490
3998
|
$ retell tests cases create --file test-case.json --llm-id llm_abc123
|
|
3491
3999
|
$ retell tests cases create --file test-case.json --flow-id cf_abc123
|
|
@@ -3498,7 +4006,8 @@ Test case JSON format:
|
|
|
3498
4006
|
"scenario": "User is calling about an order issue",
|
|
3499
4007
|
"metrics": ["response_quality", "task_completion"]
|
|
3500
4008
|
}
|
|
3501
|
-
`
|
|
4009
|
+
`
|
|
4010
|
+
).action(async (options) => {
|
|
3502
4011
|
await createTestCaseCommand({
|
|
3503
4012
|
file: options.file,
|
|
3504
4013
|
llmId: options.llmId,
|
|
@@ -3506,27 +4015,42 @@ Test case JSON format:
|
|
|
3506
4015
|
version: options.version ? parseInt(options.version, 10) : void 0
|
|
3507
4016
|
});
|
|
3508
4017
|
});
|
|
3509
|
-
testsCases.command("update <test_case_definition_id>").description("Update an existing test case definition from a JSON file").requiredOption(
|
|
4018
|
+
testsCases.command("update <test_case_definition_id>").description("Update an existing test case definition from a JSON file").requiredOption(
|
|
4019
|
+
"-f, --file <path>",
|
|
4020
|
+
"Path to JSON file containing test case updates"
|
|
4021
|
+
).addHelpText(
|
|
4022
|
+
"after",
|
|
4023
|
+
`
|
|
3510
4024
|
Examples:
|
|
3511
4025
|
$ retell tests cases update tcd_abc123 --file test-case.json
|
|
3512
|
-
`
|
|
4026
|
+
`
|
|
4027
|
+
).action(async (testCaseDefinitionId, options) => {
|
|
3513
4028
|
await updateTestCaseCommand(testCaseDefinitionId, {
|
|
3514
4029
|
file: options.file
|
|
3515
4030
|
});
|
|
3516
4031
|
});
|
|
3517
|
-
testsCases.command("delete <test_case_definition_id>").description("Delete a test case definition").addHelpText(
|
|
4032
|
+
testsCases.command("delete <test_case_definition_id>").description("Delete a test case definition").addHelpText(
|
|
4033
|
+
"after",
|
|
4034
|
+
`
|
|
3518
4035
|
Examples:
|
|
3519
4036
|
$ retell tests cases delete tcd_abc123
|
|
3520
|
-
`
|
|
4037
|
+
`
|
|
4038
|
+
).action(async (testCaseDefinitionId) => {
|
|
3521
4039
|
await deleteTestCaseCommand(testCaseDefinitionId);
|
|
3522
4040
|
});
|
|
3523
4041
|
var testsBatch = tests.command("batch").description("Manage batch tests");
|
|
3524
|
-
testsBatch.command("list").description("List all batch tests for an LLM or flow").requiredOption(
|
|
4042
|
+
testsBatch.command("list").description("List all batch tests for an LLM or flow").requiredOption(
|
|
4043
|
+
"-t, --type <type>",
|
|
4044
|
+
"Response engine type (retell-llm or conversation-flow)"
|
|
4045
|
+
).option("--llm-id <id>", "LLM ID (required when type is retell-llm)").option("--flow-id <id>", "Flow ID (required when type is conversation-flow)").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4046
|
+
"after",
|
|
4047
|
+
`
|
|
3525
4048
|
Examples:
|
|
3526
4049
|
$ retell tests batch list --type retell-llm --llm-id llm_abc123
|
|
3527
4050
|
$ retell tests batch list --type conversation-flow --flow-id cf_abc123
|
|
3528
4051
|
$ retell tests batch list --type retell-llm --llm-id llm_abc123 --fields batch_tests
|
|
3529
|
-
`
|
|
4052
|
+
`
|
|
4053
|
+
).action(async (options) => {
|
|
3530
4054
|
if (options.type !== "retell-llm" && options.type !== "conversation-flow") {
|
|
3531
4055
|
console.error('Error: type must be "retell-llm" or "conversation-flow"');
|
|
3532
4056
|
process.exit(1);
|
|
@@ -3538,21 +4062,30 @@ Examples:
|
|
|
3538
4062
|
fields: options.fields
|
|
3539
4063
|
});
|
|
3540
4064
|
});
|
|
3541
|
-
testsBatch.command("get <batch_job_id>").description("Get a specific batch test with its status and stats").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4065
|
+
testsBatch.command("get <batch_job_id>").description("Get a specific batch test with its status and stats").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4066
|
+
"after",
|
|
4067
|
+
`
|
|
3542
4068
|
Examples:
|
|
3543
4069
|
$ retell tests batch get bjj_abc123
|
|
3544
4070
|
$ retell tests batch get bjj_abc123 --fields status,stats
|
|
3545
|
-
`
|
|
4071
|
+
`
|
|
4072
|
+
).action(async (batchJobId, options) => {
|
|
3546
4073
|
await getBatchTestCommand(batchJobId, {
|
|
3547
4074
|
fields: options.fields
|
|
3548
4075
|
});
|
|
3549
4076
|
});
|
|
3550
|
-
testsBatch.command("create").description("Create a new batch test with specified test case definitions").option("--llm-id <id>", "LLM ID (mutually exclusive with --flow-id)").option("--flow-id <id>", "Flow ID (mutually exclusive with --llm-id)").requiredOption(
|
|
4077
|
+
testsBatch.command("create").description("Create a new batch test with specified test case definitions").option("--llm-id <id>", "LLM ID (mutually exclusive with --flow-id)").option("--flow-id <id>", "Flow ID (mutually exclusive with --llm-id)").requiredOption(
|
|
4078
|
+
"--cases <ids>",
|
|
4079
|
+
"Comma-separated list of test case definition IDs"
|
|
4080
|
+
).option("--version <number>", "Version of the LLM or flow (optional)").addHelpText(
|
|
4081
|
+
"after",
|
|
4082
|
+
`
|
|
3551
4083
|
Examples:
|
|
3552
4084
|
$ retell tests batch create --llm-id llm_abc123 --cases tcd_xxx,tcd_yyy,tcd_zzz
|
|
3553
4085
|
$ retell tests batch create --flow-id cf_abc123 --cases tcd_xxx,tcd_yyy
|
|
3554
4086
|
$ retell tests batch create --llm-id llm_abc123 --cases tcd_xxx --version 2
|
|
3555
|
-
`
|
|
4087
|
+
`
|
|
4088
|
+
).action(async (options) => {
|
|
3556
4089
|
await createBatchTestCommand({
|
|
3557
4090
|
llmId: options.llmId,
|
|
3558
4091
|
flowId: options.flowId,
|
|
@@ -3561,44 +4094,64 @@ Examples:
|
|
|
3561
4094
|
});
|
|
3562
4095
|
});
|
|
3563
4096
|
var testsRuns = tests.command("runs").description("View test run results");
|
|
3564
|
-
testsRuns.command("list <batch_job_id>").description("List all test runs for a batch test").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4097
|
+
testsRuns.command("list <batch_job_id>").description("List all test runs for a batch test").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4098
|
+
"after",
|
|
4099
|
+
`
|
|
3565
4100
|
Examples:
|
|
3566
4101
|
$ retell tests runs list bjj_abc123
|
|
3567
4102
|
$ retell tests runs list bjj_abc123 --fields test_runs
|
|
3568
|
-
`
|
|
4103
|
+
`
|
|
4104
|
+
).action(async (batchJobId, options) => {
|
|
3569
4105
|
await listTestRunsCommand(batchJobId, {
|
|
3570
4106
|
fields: options.fields
|
|
3571
4107
|
});
|
|
3572
4108
|
});
|
|
3573
|
-
testsRuns.command("get <test_run_id>").description("Get a specific test run result").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4109
|
+
testsRuns.command("get <test_run_id>").description("Get a specific test run result").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4110
|
+
"after",
|
|
4111
|
+
`
|
|
3574
4112
|
Examples:
|
|
3575
4113
|
$ retell tests runs get tcj_abc123
|
|
3576
4114
|
$ retell tests runs get tcj_abc123 --fields status,metric_results
|
|
3577
|
-
`
|
|
4115
|
+
`
|
|
4116
|
+
).action(async (testRunId, options) => {
|
|
3578
4117
|
await getTestRunCommand(testRunId, {
|
|
3579
4118
|
fields: options.fields
|
|
3580
4119
|
});
|
|
3581
4120
|
});
|
|
3582
4121
|
var kb = program.command("kb").description("Manage RAG knowledge bases");
|
|
3583
|
-
kb.command("list").description("List all knowledge bases").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4122
|
+
kb.command("list").description("List all knowledge bases").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4123
|
+
"after",
|
|
4124
|
+
`
|
|
3584
4125
|
Examples:
|
|
3585
4126
|
$ retell kb list
|
|
3586
4127
|
$ retell kb list --fields knowledge_base_id,knowledge_base_name,status
|
|
3587
|
-
`
|
|
4128
|
+
`
|
|
4129
|
+
).action(async (options) => {
|
|
3588
4130
|
await listKnowledgeBasesCommand({
|
|
3589
4131
|
fields: options.fields
|
|
3590
4132
|
});
|
|
3591
4133
|
});
|
|
3592
|
-
kb.command("get <knowledge_base_id>").description("Get a specific knowledge base").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4134
|
+
kb.command("get <knowledge_base_id>").description("Get a specific knowledge base").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4135
|
+
"after",
|
|
4136
|
+
`
|
|
3593
4137
|
Examples:
|
|
3594
4138
|
$ retell kb get kb_abc123
|
|
3595
4139
|
$ retell kb get kb_abc123 --fields knowledge_base_name,status,knowledge_base_sources
|
|
3596
|
-
`
|
|
4140
|
+
`
|
|
4141
|
+
).action(async (knowledgeBaseId, options) => {
|
|
3597
4142
|
await getKnowledgeBaseCommand(knowledgeBaseId, {
|
|
3598
4143
|
fields: options.fields
|
|
3599
4144
|
});
|
|
3600
4145
|
});
|
|
3601
|
-
kb.command("create").description("Create a new knowledge base").requiredOption(
|
|
4146
|
+
kb.command("create").description("Create a new knowledge base").requiredOption(
|
|
4147
|
+
"-n, --name <name>",
|
|
4148
|
+
"Knowledge base name (max 40 characters)"
|
|
4149
|
+
).option("--urls <urls>", "Comma-separated list of URLs to scrape").option(
|
|
4150
|
+
"--texts <file>",
|
|
4151
|
+
"Path to JSON file with text entries [{ title, text }, ...]"
|
|
4152
|
+
).option("--auto-refresh", "Enable 12-hour automatic refresh for URL sources").addHelpText(
|
|
4153
|
+
"after",
|
|
4154
|
+
`
|
|
3602
4155
|
Examples:
|
|
3603
4156
|
$ retell kb create --name "Product Docs"
|
|
3604
4157
|
$ retell kb create --name "Support KB" --urls https://docs.example.com,https://help.example.com
|
|
@@ -3610,7 +4163,8 @@ Text file format (texts.json):
|
|
|
3610
4163
|
{ "title": "Getting Started", "text": "Welcome to our product..." },
|
|
3611
4164
|
{ "title": "FAQ", "text": "Frequently asked questions..." }
|
|
3612
4165
|
]
|
|
3613
|
-
`
|
|
4166
|
+
`
|
|
4167
|
+
).action(async (options) => {
|
|
3614
4168
|
await createKnowledgeBaseCommand({
|
|
3615
4169
|
name: options.name,
|
|
3616
4170
|
urls: options.urls,
|
|
@@ -3618,40 +4172,61 @@ Text file format (texts.json):
|
|
|
3618
4172
|
autoRefresh: options.autoRefresh
|
|
3619
4173
|
});
|
|
3620
4174
|
});
|
|
3621
|
-
kb.command("delete <knowledge_base_id>").description("Delete a knowledge base").addHelpText(
|
|
4175
|
+
kb.command("delete <knowledge_base_id>").description("Delete a knowledge base").addHelpText(
|
|
4176
|
+
"after",
|
|
4177
|
+
`
|
|
3622
4178
|
Examples:
|
|
3623
4179
|
$ retell kb delete kb_abc123
|
|
3624
|
-
`
|
|
4180
|
+
`
|
|
4181
|
+
).action(async (knowledgeBaseId) => {
|
|
3625
4182
|
await deleteKnowledgeBaseCommand(knowledgeBaseId);
|
|
3626
4183
|
});
|
|
3627
4184
|
var kbSources = kb.command("sources").description("Manage knowledge base sources");
|
|
3628
|
-
kbSources.command("add <knowledge_base_id>").description("Add sources to an existing knowledge base").option("--urls <urls>", "Comma-separated list of URLs to scrape").option(
|
|
4185
|
+
kbSources.command("add <knowledge_base_id>").description("Add sources to an existing knowledge base").option("--urls <urls>", "Comma-separated list of URLs to scrape").option(
|
|
4186
|
+
"--texts <file>",
|
|
4187
|
+
"Path to JSON file with text entries [{ title, text }, ...]"
|
|
4188
|
+
).addHelpText(
|
|
4189
|
+
"after",
|
|
4190
|
+
`
|
|
3629
4191
|
Examples:
|
|
3630
4192
|
$ retell kb sources add kb_abc123 --urls https://docs.example.com/new
|
|
3631
4193
|
$ retell kb sources add kb_abc123 --texts additional-texts.json
|
|
3632
4194
|
$ retell kb sources add kb_abc123 --urls https://faq.example.com --texts more-texts.json
|
|
3633
|
-
`
|
|
4195
|
+
`
|
|
4196
|
+
).action(async (knowledgeBaseId, options) => {
|
|
3634
4197
|
await addKnowledgeBaseSourcesCommand(knowledgeBaseId, {
|
|
3635
4198
|
urls: options.urls,
|
|
3636
4199
|
texts: options.texts
|
|
3637
4200
|
});
|
|
3638
4201
|
});
|
|
3639
|
-
kbSources.command("delete <knowledge_base_id> <source_id>").description("Remove a source from a knowledge base").addHelpText(
|
|
4202
|
+
kbSources.command("delete <knowledge_base_id> <source_id>").description("Remove a source from a knowledge base").addHelpText(
|
|
4203
|
+
"after",
|
|
4204
|
+
`
|
|
3640
4205
|
Examples:
|
|
3641
4206
|
$ retell kb sources delete kb_abc123 source_xyz789
|
|
3642
|
-
`
|
|
4207
|
+
`
|
|
4208
|
+
).action(async (knowledgeBaseId, sourceId) => {
|
|
3643
4209
|
await deleteKnowledgeBaseSourceCommand(knowledgeBaseId, sourceId);
|
|
3644
4210
|
});
|
|
3645
4211
|
var flows = program.command("flows").description("Manage conversation flow response engines");
|
|
3646
|
-
flows.command("list").description("List all conversation flows").option(
|
|
4212
|
+
flows.command("list").description("List all conversation flows").option(
|
|
4213
|
+
"-l, --limit <number>",
|
|
4214
|
+
"Maximum number of flows to return (default: 100, max: 1000)",
|
|
4215
|
+
"100"
|
|
4216
|
+
).option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4217
|
+
"after",
|
|
4218
|
+
`
|
|
3647
4219
|
Examples:
|
|
3648
4220
|
$ retell flows list
|
|
3649
4221
|
$ retell flows list --limit 50
|
|
3650
4222
|
$ retell flows list --fields conversation_flow_id,version,start_speaker
|
|
3651
|
-
`
|
|
4223
|
+
`
|
|
4224
|
+
).action(async (options) => {
|
|
3652
4225
|
const limit = parseInt(options.limit, 10);
|
|
3653
4226
|
if (isNaN(limit) || limit < 1 || limit > 1e3) {
|
|
3654
|
-
console.error(
|
|
4227
|
+
console.error(
|
|
4228
|
+
"Error: limit must be a positive number between 1 and 1000"
|
|
4229
|
+
);
|
|
3655
4230
|
process.exit(1);
|
|
3656
4231
|
}
|
|
3657
4232
|
await listFlowsCommand({
|
|
@@ -3659,18 +4234,29 @@ Examples:
|
|
|
3659
4234
|
fields: options.fields
|
|
3660
4235
|
});
|
|
3661
4236
|
});
|
|
3662
|
-
flows.command("get <conversation_flow_id>").description("Get a specific conversation flow").option(
|
|
4237
|
+
flows.command("get <conversation_flow_id>").description("Get a specific conversation flow").option(
|
|
4238
|
+
"--version <number>",
|
|
4239
|
+
"Specific version to retrieve (defaults to latest)"
|
|
4240
|
+
).option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4241
|
+
"after",
|
|
4242
|
+
`
|
|
3663
4243
|
Examples:
|
|
3664
4244
|
$ retell flows get cf_abc123
|
|
3665
4245
|
$ retell flows get cf_abc123 --version 2
|
|
3666
4246
|
$ retell flows get cf_abc123 --fields conversation_flow_id,nodes,edges
|
|
3667
|
-
`
|
|
4247
|
+
`
|
|
4248
|
+
).action(async (conversationFlowId, options) => {
|
|
3668
4249
|
await getFlowCommand(conversationFlowId, {
|
|
3669
4250
|
version: options.version ? parseInt(options.version, 10) : void 0,
|
|
3670
4251
|
fields: options.fields
|
|
3671
4252
|
});
|
|
3672
4253
|
});
|
|
3673
|
-
flows.command("create").description("Create a new conversation flow from a JSON file").requiredOption(
|
|
4254
|
+
flows.command("create").description("Create a new conversation flow from a JSON file").requiredOption(
|
|
4255
|
+
"-f, --file <path>",
|
|
4256
|
+
"Path to JSON file containing flow configuration"
|
|
4257
|
+
).addHelpText(
|
|
4258
|
+
"after",
|
|
4259
|
+
`
|
|
3674
4260
|
Examples:
|
|
3675
4261
|
$ retell flows create --file flow.json
|
|
3676
4262
|
|
|
@@ -3681,25 +4267,38 @@ Flow JSON format (minimal):
|
|
|
3681
4267
|
"nodes": [...],
|
|
3682
4268
|
"edges": [...]
|
|
3683
4269
|
}
|
|
3684
|
-
`
|
|
4270
|
+
`
|
|
4271
|
+
).action(async (options) => {
|
|
3685
4272
|
await createFlowCommand({
|
|
3686
4273
|
file: options.file
|
|
3687
4274
|
});
|
|
3688
4275
|
});
|
|
3689
|
-
flows.command("update <conversation_flow_id>").description("Update an existing conversation flow from a JSON file").requiredOption(
|
|
4276
|
+
flows.command("update <conversation_flow_id>").description("Update an existing conversation flow from a JSON file").requiredOption(
|
|
4277
|
+
"-f, --file <path>",
|
|
4278
|
+
"Path to JSON file containing flow updates"
|
|
4279
|
+
).option(
|
|
4280
|
+
"--version <number>",
|
|
4281
|
+
"Specific version to update (defaults to latest)"
|
|
4282
|
+
).addHelpText(
|
|
4283
|
+
"after",
|
|
4284
|
+
`
|
|
3690
4285
|
Examples:
|
|
3691
4286
|
$ retell flows update cf_abc123 --file updates.json
|
|
3692
4287
|
$ retell flows update cf_abc123 --file updates.json --version 2
|
|
3693
|
-
`
|
|
4288
|
+
`
|
|
4289
|
+
).action(async (conversationFlowId, options) => {
|
|
3694
4290
|
await updateFlowCommand(conversationFlowId, {
|
|
3695
4291
|
file: options.file,
|
|
3696
4292
|
version: options.version ? parseInt(options.version, 10) : void 0
|
|
3697
4293
|
});
|
|
3698
4294
|
});
|
|
3699
|
-
flows.command("delete <conversation_flow_id>").description("Delete a conversation flow").addHelpText(
|
|
4295
|
+
flows.command("delete <conversation_flow_id>").description("Delete a conversation flow").addHelpText(
|
|
4296
|
+
"after",
|
|
4297
|
+
`
|
|
3700
4298
|
Examples:
|
|
3701
4299
|
$ retell flows delete cf_abc123
|
|
3702
|
-
`
|
|
4300
|
+
`
|
|
4301
|
+
).action(async (conversationFlowId) => {
|
|
3703
4302
|
await deleteFlowCommand(conversationFlowId);
|
|
3704
4303
|
});
|
|
3705
4304
|
program.parse(process.argv);
|