retell-cli 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +881 -288
- 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,8 +2932,7 @@ 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,
|
|
@@ -2723,7 +2962,10 @@ var import_fs10 = require("fs");
|
|
|
2723
2962
|
async function updateTestCaseCommand(testCaseDefinitionId, options) {
|
|
2724
2963
|
try {
|
|
2725
2964
|
if (!(0, import_fs10.existsSync)(options.file)) {
|
|
2726
|
-
outputError(
|
|
2965
|
+
outputError(
|
|
2966
|
+
`Test case file not found: ${options.file}`,
|
|
2967
|
+
"FILE_NOT_FOUND"
|
|
2968
|
+
);
|
|
2727
2969
|
return;
|
|
2728
2970
|
}
|
|
2729
2971
|
let input;
|
|
@@ -2732,9 +2974,15 @@ async function updateTestCaseCommand(testCaseDefinitionId, options) {
|
|
|
2732
2974
|
input = JSON.parse(content);
|
|
2733
2975
|
} catch (error) {
|
|
2734
2976
|
if (error instanceof SyntaxError) {
|
|
2735
|
-
outputError(
|
|
2977
|
+
outputError(
|
|
2978
|
+
`Invalid JSON in test case file: ${error.message}`,
|
|
2979
|
+
"INVALID_JSON"
|
|
2980
|
+
);
|
|
2736
2981
|
} else {
|
|
2737
|
-
outputError(
|
|
2982
|
+
outputError(
|
|
2983
|
+
`Error reading test case file: ${error.message}`,
|
|
2984
|
+
"FILE_READ_ERROR"
|
|
2985
|
+
);
|
|
2738
2986
|
}
|
|
2739
2987
|
return;
|
|
2740
2988
|
}
|
|
@@ -2779,13 +3027,19 @@ async function deleteTestCaseCommand(testCaseDefinitionId) {
|
|
|
2779
3027
|
function buildResponseEngine3(options) {
|
|
2780
3028
|
if (options.type === "retell-llm") {
|
|
2781
3029
|
if (!options.llmId) {
|
|
2782
|
-
outputError(
|
|
3030
|
+
outputError(
|
|
3031
|
+
"--llm-id is required when type is retell-llm",
|
|
3032
|
+
"MISSING_PARAMETER"
|
|
3033
|
+
);
|
|
2783
3034
|
return null;
|
|
2784
3035
|
}
|
|
2785
3036
|
return { type: "retell-llm", llm_id: options.llmId };
|
|
2786
3037
|
} else {
|
|
2787
3038
|
if (!options.flowId) {
|
|
2788
|
-
outputError(
|
|
3039
|
+
outputError(
|
|
3040
|
+
"--flow-id is required when type is conversation-flow",
|
|
3041
|
+
"MISSING_PARAMETER"
|
|
3042
|
+
);
|
|
2789
3043
|
return null;
|
|
2790
3044
|
}
|
|
2791
3045
|
return { type: "conversation-flow", conversation_flow_id: options.flowId };
|
|
@@ -2794,8 +3048,7 @@ function buildResponseEngine3(options) {
|
|
|
2794
3048
|
async function listBatchTestsCommand(options) {
|
|
2795
3049
|
try {
|
|
2796
3050
|
const responseEngine = buildResponseEngine3(options);
|
|
2797
|
-
if (!responseEngine)
|
|
2798
|
-
return;
|
|
3051
|
+
if (!responseEngine) return;
|
|
2799
3052
|
const batchTests = await listBatchTests(responseEngine);
|
|
2800
3053
|
const output = {
|
|
2801
3054
|
response_engine: responseEngine,
|
|
@@ -2803,7 +3056,10 @@ async function listBatchTestsCommand(options) {
|
|
|
2803
3056
|
total_count: (batchTests || []).length
|
|
2804
3057
|
};
|
|
2805
3058
|
if (options.fields) {
|
|
2806
|
-
const filtered = filterFields(
|
|
3059
|
+
const filtered = filterFields(
|
|
3060
|
+
output,
|
|
3061
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3062
|
+
);
|
|
2807
3063
|
outputJson(filtered);
|
|
2808
3064
|
} else {
|
|
2809
3065
|
outputJson(output);
|
|
@@ -2818,7 +3074,10 @@ async function getBatchTestCommand(batchJobId, options) {
|
|
|
2818
3074
|
try {
|
|
2819
3075
|
const batchTest = await getBatchTest(batchJobId);
|
|
2820
3076
|
if (options.fields) {
|
|
2821
|
-
const filtered = filterFields(
|
|
3077
|
+
const filtered = filterFields(
|
|
3078
|
+
batchTest,
|
|
3079
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3080
|
+
);
|
|
2822
3081
|
outputJson(filtered);
|
|
2823
3082
|
} else {
|
|
2824
3083
|
outputJson(batchTest);
|
|
@@ -2831,21 +3090,33 @@ async function getBatchTestCommand(batchJobId, options) {
|
|
|
2831
3090
|
// src/commands/tests/batch/create.ts
|
|
2832
3091
|
function buildResponseEngine4(options) {
|
|
2833
3092
|
if (options.llmId && options.flowId) {
|
|
2834
|
-
outputError(
|
|
3093
|
+
outputError(
|
|
3094
|
+
"Cannot specify both --llm-id and --flow-id",
|
|
3095
|
+
"INVALID_PARAMETERS"
|
|
3096
|
+
);
|
|
2835
3097
|
return null;
|
|
2836
3098
|
}
|
|
2837
3099
|
if (!options.llmId && !options.flowId) {
|
|
2838
|
-
outputError(
|
|
3100
|
+
outputError(
|
|
3101
|
+
"Either --llm-id or --flow-id is required",
|
|
3102
|
+
"MISSING_PARAMETER"
|
|
3103
|
+
);
|
|
2839
3104
|
return null;
|
|
2840
3105
|
}
|
|
2841
3106
|
if (options.llmId) {
|
|
2842
|
-
const engine = {
|
|
3107
|
+
const engine = {
|
|
3108
|
+
type: "retell-llm",
|
|
3109
|
+
llm_id: options.llmId
|
|
3110
|
+
};
|
|
2843
3111
|
if (options.version !== void 0) {
|
|
2844
3112
|
engine.version = options.version;
|
|
2845
3113
|
}
|
|
2846
3114
|
return engine;
|
|
2847
3115
|
} else {
|
|
2848
|
-
const engine = {
|
|
3116
|
+
const engine = {
|
|
3117
|
+
type: "conversation-flow",
|
|
3118
|
+
conversation_flow_id: options.flowId
|
|
3119
|
+
};
|
|
2849
3120
|
if (options.version !== void 0) {
|
|
2850
3121
|
engine.version = options.version;
|
|
2851
3122
|
}
|
|
@@ -2855,11 +3126,13 @@ function buildResponseEngine4(options) {
|
|
|
2855
3126
|
async function createBatchTestCommand(options) {
|
|
2856
3127
|
try {
|
|
2857
3128
|
const responseEngine = buildResponseEngine4(options);
|
|
2858
|
-
if (!responseEngine)
|
|
2859
|
-
return;
|
|
3129
|
+
if (!responseEngine) return;
|
|
2860
3130
|
const testCaseDefinitionIds = options.cases.split(",").map((id) => id.trim()).filter(Boolean);
|
|
2861
3131
|
if (testCaseDefinitionIds.length === 0) {
|
|
2862
|
-
outputError(
|
|
3132
|
+
outputError(
|
|
3133
|
+
"At least one test case definition ID is required",
|
|
3134
|
+
"MISSING_PARAMETER"
|
|
3135
|
+
);
|
|
2863
3136
|
return;
|
|
2864
3137
|
}
|
|
2865
3138
|
const batchTest = await createBatchTest({
|
|
@@ -2889,7 +3162,10 @@ async function listTestRunsCommand(batchJobId, options) {
|
|
|
2889
3162
|
total_count: (testRuns || []).length
|
|
2890
3163
|
};
|
|
2891
3164
|
if (options.fields) {
|
|
2892
|
-
const filtered = filterFields(
|
|
3165
|
+
const filtered = filterFields(
|
|
3166
|
+
output,
|
|
3167
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3168
|
+
);
|
|
2893
3169
|
outputJson(filtered);
|
|
2894
3170
|
} else {
|
|
2895
3171
|
outputJson(output);
|
|
@@ -2904,7 +3180,10 @@ async function getTestRunCommand(testRunId, options) {
|
|
|
2904
3180
|
try {
|
|
2905
3181
|
const testRun = await getTestRun(testRunId);
|
|
2906
3182
|
if (options.fields) {
|
|
2907
|
-
const filtered = filterFields(
|
|
3183
|
+
const filtered = filterFields(
|
|
3184
|
+
testRun,
|
|
3185
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3186
|
+
);
|
|
2908
3187
|
outputJson(filtered);
|
|
2909
3188
|
} else {
|
|
2910
3189
|
outputJson(testRun);
|
|
@@ -2919,7 +3198,10 @@ async function listKnowledgeBasesCommand(options) {
|
|
|
2919
3198
|
try {
|
|
2920
3199
|
const client = getRetellClient();
|
|
2921
3200
|
const knowledgeBases = await client.knowledgeBase.list();
|
|
2922
|
-
const output = options.fields ? filterFields(
|
|
3201
|
+
const output = options.fields ? filterFields(
|
|
3202
|
+
knowledgeBases,
|
|
3203
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3204
|
+
) : knowledgeBases;
|
|
2923
3205
|
outputJson(output);
|
|
2924
3206
|
} catch (error) {
|
|
2925
3207
|
handleSdkError(error);
|
|
@@ -2931,7 +3213,10 @@ async function getKnowledgeBaseCommand(knowledgeBaseId, options) {
|
|
|
2931
3213
|
try {
|
|
2932
3214
|
const client = getRetellClient();
|
|
2933
3215
|
const knowledgeBase = await client.knowledgeBase.retrieve(knowledgeBaseId);
|
|
2934
|
-
const output = options.fields ? filterFields(
|
|
3216
|
+
const output = options.fields ? filterFields(
|
|
3217
|
+
knowledgeBase,
|
|
3218
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3219
|
+
) : knowledgeBase;
|
|
2935
3220
|
outputJson(output);
|
|
2936
3221
|
} catch (error) {
|
|
2937
3222
|
handleSdkError(error);
|
|
@@ -2943,7 +3228,10 @@ var import_fs11 = require("fs");
|
|
|
2943
3228
|
async function createKnowledgeBaseCommand(options) {
|
|
2944
3229
|
try {
|
|
2945
3230
|
if (options.name.length > 40) {
|
|
2946
|
-
outputError(
|
|
3231
|
+
outputError(
|
|
3232
|
+
"Knowledge base name must be 40 characters or less",
|
|
3233
|
+
"INVALID_NAME"
|
|
3234
|
+
);
|
|
2947
3235
|
return;
|
|
2948
3236
|
}
|
|
2949
3237
|
const createParams = {
|
|
@@ -2967,27 +3255,40 @@ async function createKnowledgeBaseCommand(options) {
|
|
|
2967
3255
|
const content = (0, import_fs11.readFileSync)(options.texts, "utf-8");
|
|
2968
3256
|
const textsData = JSON.parse(content);
|
|
2969
3257
|
if (!Array.isArray(textsData)) {
|
|
2970
|
-
outputError(
|
|
3258
|
+
outputError(
|
|
3259
|
+
"Texts file must contain an array of { title, text } objects",
|
|
3260
|
+
"INVALID_TEXTS"
|
|
3261
|
+
);
|
|
2971
3262
|
return;
|
|
2972
3263
|
}
|
|
2973
|
-
const texts = textsData.map(
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
3264
|
+
const texts = textsData.map(
|
|
3265
|
+
(entry, index) => {
|
|
3266
|
+
if (typeof entry !== "object" || entry === null) {
|
|
3267
|
+
throw new Error(`Entry at index ${index} must be an object`);
|
|
3268
|
+
}
|
|
3269
|
+
const e = entry;
|
|
3270
|
+
if (typeof e.title !== "string" || typeof e.text !== "string") {
|
|
3271
|
+
throw new Error(
|
|
3272
|
+
`Entry at index ${index} must have "title" and "text" string fields`
|
|
3273
|
+
);
|
|
3274
|
+
}
|
|
3275
|
+
return { title: e.title, text: e.text };
|
|
2980
3276
|
}
|
|
2981
|
-
|
|
2982
|
-
});
|
|
3277
|
+
);
|
|
2983
3278
|
if (texts.length > 0) {
|
|
2984
3279
|
createParams.knowledge_base_texts = texts;
|
|
2985
3280
|
}
|
|
2986
3281
|
} catch (error) {
|
|
2987
3282
|
if (error instanceof SyntaxError) {
|
|
2988
|
-
outputError(
|
|
3283
|
+
outputError(
|
|
3284
|
+
`Invalid JSON in texts file: ${error.message}`,
|
|
3285
|
+
"INVALID_JSON"
|
|
3286
|
+
);
|
|
2989
3287
|
} else if (error instanceof Error) {
|
|
2990
|
-
outputError(
|
|
3288
|
+
outputError(
|
|
3289
|
+
`Error parsing texts file: ${error.message}`,
|
|
3290
|
+
"INVALID_TEXTS"
|
|
3291
|
+
);
|
|
2991
3292
|
}
|
|
2992
3293
|
return;
|
|
2993
3294
|
}
|
|
@@ -3031,7 +3332,10 @@ var import_fs12 = require("fs");
|
|
|
3031
3332
|
async function addKnowledgeBaseSourcesCommand(knowledgeBaseId, options) {
|
|
3032
3333
|
try {
|
|
3033
3334
|
if (!options.urls && !options.texts) {
|
|
3034
|
-
outputError(
|
|
3335
|
+
outputError(
|
|
3336
|
+
"At least one of --urls or --texts must be provided",
|
|
3337
|
+
"MISSING_SOURCES"
|
|
3338
|
+
);
|
|
3035
3339
|
return;
|
|
3036
3340
|
}
|
|
3037
3341
|
const addParams = {};
|
|
@@ -3050,33 +3354,49 @@ async function addKnowledgeBaseSourcesCommand(knowledgeBaseId, options) {
|
|
|
3050
3354
|
const content = (0, import_fs12.readFileSync)(options.texts, "utf-8");
|
|
3051
3355
|
const textsData = JSON.parse(content);
|
|
3052
3356
|
if (!Array.isArray(textsData)) {
|
|
3053
|
-
outputError(
|
|
3357
|
+
outputError(
|
|
3358
|
+
"Texts file must contain an array of { title, text } objects",
|
|
3359
|
+
"INVALID_TEXTS"
|
|
3360
|
+
);
|
|
3054
3361
|
return;
|
|
3055
3362
|
}
|
|
3056
|
-
const texts = textsData.map(
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3363
|
+
const texts = textsData.map(
|
|
3364
|
+
(entry, index) => {
|
|
3365
|
+
if (typeof entry !== "object" || entry === null) {
|
|
3366
|
+
throw new Error(`Entry at index ${index} must be an object`);
|
|
3367
|
+
}
|
|
3368
|
+
const e = entry;
|
|
3369
|
+
if (typeof e.title !== "string" || typeof e.text !== "string") {
|
|
3370
|
+
throw new Error(
|
|
3371
|
+
`Entry at index ${index} must have "title" and "text" string fields`
|
|
3372
|
+
);
|
|
3373
|
+
}
|
|
3374
|
+
return { title: e.title, text: e.text };
|
|
3063
3375
|
}
|
|
3064
|
-
|
|
3065
|
-
});
|
|
3376
|
+
);
|
|
3066
3377
|
if (texts.length > 0) {
|
|
3067
3378
|
addParams.knowledge_base_texts = texts;
|
|
3068
3379
|
}
|
|
3069
3380
|
} catch (error) {
|
|
3070
3381
|
if (error instanceof SyntaxError) {
|
|
3071
|
-
outputError(
|
|
3382
|
+
outputError(
|
|
3383
|
+
`Invalid JSON in texts file: ${error.message}`,
|
|
3384
|
+
"INVALID_JSON"
|
|
3385
|
+
);
|
|
3072
3386
|
} else if (error instanceof Error) {
|
|
3073
|
-
outputError(
|
|
3387
|
+
outputError(
|
|
3388
|
+
`Error parsing texts file: ${error.message}`,
|
|
3389
|
+
"INVALID_TEXTS"
|
|
3390
|
+
);
|
|
3074
3391
|
}
|
|
3075
3392
|
return;
|
|
3076
3393
|
}
|
|
3077
3394
|
}
|
|
3078
3395
|
const client = getRetellClient();
|
|
3079
|
-
const knowledgeBase = await client.knowledgeBase.addSources(
|
|
3396
|
+
const knowledgeBase = await client.knowledgeBase.addSources(
|
|
3397
|
+
knowledgeBaseId,
|
|
3398
|
+
addParams
|
|
3399
|
+
);
|
|
3080
3400
|
const output = {
|
|
3081
3401
|
message: "Sources added successfully",
|
|
3082
3402
|
knowledge_base_id: knowledgeBase.knowledge_base_id,
|
|
@@ -3097,7 +3417,10 @@ async function addKnowledgeBaseSourcesCommand(knowledgeBaseId, options) {
|
|
|
3097
3417
|
async function deleteKnowledgeBaseSourceCommand(knowledgeBaseId, sourceId) {
|
|
3098
3418
|
try {
|
|
3099
3419
|
const client = getRetellClient();
|
|
3100
|
-
const knowledgeBase = await client.knowledgeBase.deleteSource(
|
|
3420
|
+
const knowledgeBase = await client.knowledgeBase.deleteSource(
|
|
3421
|
+
knowledgeBaseId,
|
|
3422
|
+
sourceId
|
|
3423
|
+
);
|
|
3101
3424
|
const output = {
|
|
3102
3425
|
message: "Source deleted successfully",
|
|
3103
3426
|
knowledge_base_id: knowledgeBase.knowledge_base_id,
|
|
@@ -3121,7 +3444,10 @@ async function listFlowsCommand(options) {
|
|
|
3121
3444
|
const flows2 = await client.conversationFlow.list({
|
|
3122
3445
|
limit: options.limit || 100
|
|
3123
3446
|
});
|
|
3124
|
-
const output = options.fields ? filterFields(
|
|
3447
|
+
const output = options.fields ? filterFields(
|
|
3448
|
+
flows2,
|
|
3449
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3450
|
+
) : flows2;
|
|
3125
3451
|
outputJson(output);
|
|
3126
3452
|
} catch (error) {
|
|
3127
3453
|
handleSdkError(error);
|
|
@@ -3136,8 +3462,14 @@ async function getFlowCommand(conversationFlowId, options) {
|
|
|
3136
3462
|
if (options.version !== void 0) {
|
|
3137
3463
|
retrieveOptions.version = options.version;
|
|
3138
3464
|
}
|
|
3139
|
-
const flow = await client.conversationFlow.retrieve(
|
|
3140
|
-
|
|
3465
|
+
const flow = await client.conversationFlow.retrieve(
|
|
3466
|
+
conversationFlowId,
|
|
3467
|
+
retrieveOptions
|
|
3468
|
+
);
|
|
3469
|
+
const output = options.fields ? filterFields(
|
|
3470
|
+
flow,
|
|
3471
|
+
options.fields.split(",").map((f) => f.trim())
|
|
3472
|
+
) : flow;
|
|
3141
3473
|
outputJson(output);
|
|
3142
3474
|
} catch (error) {
|
|
3143
3475
|
handleSdkError(error);
|
|
@@ -3158,14 +3490,23 @@ async function createFlowCommand(options) {
|
|
|
3158
3490
|
flowConfig = JSON.parse(content);
|
|
3159
3491
|
} catch (error) {
|
|
3160
3492
|
if (error instanceof SyntaxError) {
|
|
3161
|
-
outputError(
|
|
3493
|
+
outputError(
|
|
3494
|
+
`Invalid JSON in flow file: ${error.message}`,
|
|
3495
|
+
"INVALID_JSON"
|
|
3496
|
+
);
|
|
3162
3497
|
} else if (error instanceof Error) {
|
|
3163
|
-
outputError(
|
|
3498
|
+
outputError(
|
|
3499
|
+
`Error reading flow file: ${error.message}`,
|
|
3500
|
+
"FILE_READ_ERROR"
|
|
3501
|
+
);
|
|
3164
3502
|
}
|
|
3165
3503
|
return;
|
|
3166
3504
|
}
|
|
3167
3505
|
if (!flowConfig.start_speaker) {
|
|
3168
|
-
outputError(
|
|
3506
|
+
outputError(
|
|
3507
|
+
'Flow configuration must have a "start_speaker" field (user or agent)',
|
|
3508
|
+
"INVALID_FLOW"
|
|
3509
|
+
);
|
|
3169
3510
|
return;
|
|
3170
3511
|
}
|
|
3171
3512
|
const client = getRetellClient();
|
|
@@ -3196,9 +3537,15 @@ async function updateFlowCommand(conversationFlowId, options) {
|
|
|
3196
3537
|
flowUpdates = JSON.parse(content);
|
|
3197
3538
|
} catch (error) {
|
|
3198
3539
|
if (error instanceof SyntaxError) {
|
|
3199
|
-
outputError(
|
|
3540
|
+
outputError(
|
|
3541
|
+
`Invalid JSON in flow file: ${error.message}`,
|
|
3542
|
+
"INVALID_JSON"
|
|
3543
|
+
);
|
|
3200
3544
|
} else if (error instanceof Error) {
|
|
3201
|
-
outputError(
|
|
3545
|
+
outputError(
|
|
3546
|
+
`Error reading flow file: ${error.message}`,
|
|
3547
|
+
"FILE_READ_ERROR"
|
|
3548
|
+
);
|
|
3202
3549
|
}
|
|
3203
3550
|
return;
|
|
3204
3551
|
}
|
|
@@ -3206,7 +3553,10 @@ async function updateFlowCommand(conversationFlowId, options) {
|
|
|
3206
3553
|
flowUpdates.version = options.version;
|
|
3207
3554
|
}
|
|
3208
3555
|
const client = getRetellClient();
|
|
3209
|
-
const flow = await client.conversationFlow.update(
|
|
3556
|
+
const flow = await client.conversationFlow.update(
|
|
3557
|
+
conversationFlowId,
|
|
3558
|
+
flowUpdates
|
|
3559
|
+
);
|
|
3210
3560
|
const output = {
|
|
3211
3561
|
message: "Conversation flow updated successfully",
|
|
3212
3562
|
conversation_flow_id: flow.conversation_flow_id,
|
|
@@ -3241,22 +3591,35 @@ var packageJson = JSON.parse(
|
|
|
3241
3591
|
);
|
|
3242
3592
|
var program = new import_commander.Command();
|
|
3243
3593
|
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(
|
|
3594
|
+
program.command("login").description("Authenticate with Retell AI").addHelpText(
|
|
3595
|
+
"after",
|
|
3596
|
+
`
|
|
3245
3597
|
Examples:
|
|
3246
3598
|
$ retell login
|
|
3247
3599
|
# Enter your API key when prompted
|
|
3248
3600
|
# Creates .retellrc.json in current directory
|
|
3249
|
-
`
|
|
3601
|
+
`
|
|
3602
|
+
).action(async () => {
|
|
3250
3603
|
await loginCommand();
|
|
3251
3604
|
});
|
|
3252
3605
|
var transcripts = program.command("transcripts").description("Manage call transcripts");
|
|
3253
|
-
transcripts.command("list").description("List all call transcripts").option(
|
|
3606
|
+
transcripts.command("list").description("List all call transcripts").option(
|
|
3607
|
+
"-l, --limit <number>",
|
|
3608
|
+
"Maximum number of calls to return (default: 50)",
|
|
3609
|
+
"50"
|
|
3610
|
+
).option(
|
|
3611
|
+
"--fields <fields>",
|
|
3612
|
+
"Comma-separated list of fields to return (e.g., call_id,call_status,metadata.duration)"
|
|
3613
|
+
).addHelpText(
|
|
3614
|
+
"after",
|
|
3615
|
+
`
|
|
3254
3616
|
Examples:
|
|
3255
3617
|
$ retell transcripts list
|
|
3256
3618
|
$ retell transcripts list --limit 100
|
|
3257
3619
|
$ retell transcripts list --fields call_id,call_status
|
|
3258
3620
|
$ retell transcripts list | jq '.[] | select(.call_status == "error")'
|
|
3259
|
-
`
|
|
3621
|
+
`
|
|
3622
|
+
).action(async (options) => {
|
|
3260
3623
|
const limit = parseInt(options.limit, 10);
|
|
3261
3624
|
if (isNaN(limit) || limit < 1) {
|
|
3262
3625
|
console.error("Error: limit must be a positive number");
|
|
@@ -3267,17 +3630,44 @@ Examples:
|
|
|
3267
3630
|
fields: options.fields
|
|
3268
3631
|
});
|
|
3269
3632
|
});
|
|
3270
|
-
transcripts.command("get <call_id>").description("Get a specific call transcript").option(
|
|
3633
|
+
transcripts.command("get <call_id>").description("Get a specific call transcript").option(
|
|
3634
|
+
"--fields <fields>",
|
|
3635
|
+
"Comma-separated list of fields to return (e.g., call_id,metadata.duration,analysis)"
|
|
3636
|
+
).addHelpText(
|
|
3637
|
+
"after",
|
|
3638
|
+
`
|
|
3271
3639
|
Examples:
|
|
3272
3640
|
$ retell transcripts get call_abc123
|
|
3273
3641
|
$ retell transcripts get call_abc123 --fields call_id,metadata.duration
|
|
3274
3642
|
$ retell transcripts get call_abc123 | jq '.transcript_object'
|
|
3275
|
-
`
|
|
3643
|
+
`
|
|
3644
|
+
).action(async (callId, options) => {
|
|
3276
3645
|
await getTranscriptCommand(callId, {
|
|
3277
3646
|
fields: options.fields
|
|
3278
3647
|
});
|
|
3279
3648
|
});
|
|
3280
|
-
transcripts.command("analyze <call_id>").description(
|
|
3649
|
+
transcripts.command("analyze <call_id>").description(
|
|
3650
|
+
"Analyze a call transcript with performance metrics and insights"
|
|
3651
|
+
).option(
|
|
3652
|
+
"--fields <fields>",
|
|
3653
|
+
"Comma-separated list of fields to return (e.g., call_id,performance,analysis.summary)"
|
|
3654
|
+
).option(
|
|
3655
|
+
"--raw",
|
|
3656
|
+
"Return unmodified API response instead of enriched analysis"
|
|
3657
|
+
).option(
|
|
3658
|
+
"--hotspots-only",
|
|
3659
|
+
"Return only conversation hotspots/issues for troubleshooting"
|
|
3660
|
+
).option(
|
|
3661
|
+
"--latency-threshold <ms>",
|
|
3662
|
+
`Latency threshold in ms for hotspot detection (default: ${DEFAULT_LATENCY_THRESHOLD})`,
|
|
3663
|
+
String(DEFAULT_LATENCY_THRESHOLD)
|
|
3664
|
+
).option(
|
|
3665
|
+
"--silence-threshold <ms>",
|
|
3666
|
+
`Silence threshold in ms for hotspot detection (default: ${DEFAULT_SILENCE_THRESHOLD})`,
|
|
3667
|
+
String(DEFAULT_SILENCE_THRESHOLD)
|
|
3668
|
+
).addHelpText(
|
|
3669
|
+
"after",
|
|
3670
|
+
`
|
|
3281
3671
|
Examples:
|
|
3282
3672
|
$ retell transcripts analyze call_abc123
|
|
3283
3673
|
$ retell transcripts analyze call_abc123 --fields call_id,performance
|
|
@@ -3287,7 +3677,8 @@ Examples:
|
|
|
3287
3677
|
$ retell transcripts analyze call_abc123 --hotspots-only --latency-threshold 1500
|
|
3288
3678
|
$ retell transcripts analyze call_abc123 --hotspots-only --fields hotspots
|
|
3289
3679
|
$ retell transcripts analyze call_abc123 | jq '.performance.latency_p50_ms'
|
|
3290
|
-
`
|
|
3680
|
+
`
|
|
3681
|
+
).action(async (callId, options) => {
|
|
3291
3682
|
await analyzeTranscriptCommand(callId, {
|
|
3292
3683
|
fields: options.fields,
|
|
3293
3684
|
raw: options.raw,
|
|
@@ -3296,14 +3687,23 @@ Examples:
|
|
|
3296
3687
|
silenceThreshold: options.silenceThreshold ? parseInt(options.silenceThreshold) : void 0
|
|
3297
3688
|
});
|
|
3298
3689
|
});
|
|
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(
|
|
3690
|
+
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(
|
|
3691
|
+
"--since <date>",
|
|
3692
|
+
"Filter calls after this date (YYYY-MM-DD or ISO format)"
|
|
3693
|
+
).option(
|
|
3694
|
+
"--until <date>",
|
|
3695
|
+
"Filter calls before this date (YYYY-MM-DD or ISO format)"
|
|
3696
|
+
).option("--limit <number>", "Maximum number of results (default: 50)", "50").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
3697
|
+
"after",
|
|
3698
|
+
`
|
|
3300
3699
|
Examples:
|
|
3301
3700
|
$ retell transcripts search --status error
|
|
3302
3701
|
$ retell transcripts search --agent-id agent_123 --since 2025-11-01
|
|
3303
3702
|
$ retell transcripts search --status error --limit 10
|
|
3304
3703
|
$ retell transcripts search --status error --fields call_id,agent_id,call_status
|
|
3305
3704
|
$ retell transcripts search --since 2025-11-01 --until 2025-11-15
|
|
3306
|
-
`
|
|
3705
|
+
`
|
|
3706
|
+
).action(async (options) => {
|
|
3307
3707
|
await searchTranscriptsCommand({
|
|
3308
3708
|
status: options.status,
|
|
3309
3709
|
agentId: options.agentId,
|
|
@@ -3314,13 +3714,23 @@ Examples:
|
|
|
3314
3714
|
});
|
|
3315
3715
|
});
|
|
3316
3716
|
var agents = program.command("agents").description("Manage agents");
|
|
3317
|
-
agents.command("list").description("List all agents").option(
|
|
3717
|
+
agents.command("list").description("List all agents").option(
|
|
3718
|
+
"-l, --limit <number>",
|
|
3719
|
+
"Maximum number of agents to return (default: 100)",
|
|
3720
|
+
"100"
|
|
3721
|
+
).option(
|
|
3722
|
+
"--fields <fields>",
|
|
3723
|
+
"Comma-separated list of fields to return (e.g., agent_id,agent_name,response_engine_type)"
|
|
3724
|
+
).addHelpText(
|
|
3725
|
+
"after",
|
|
3726
|
+
`
|
|
3318
3727
|
Examples:
|
|
3319
3728
|
$ retell agents list
|
|
3320
3729
|
$ retell agents list --limit 10
|
|
3321
3730
|
$ retell agents list --fields agent_id,agent_name
|
|
3322
3731
|
$ retell agents list | jq '.[] | select(.response_engine.type == "retell-llm")'
|
|
3323
|
-
`
|
|
3732
|
+
`
|
|
3733
|
+
).action(async (options) => {
|
|
3324
3734
|
const limit = parseInt(options.limit, 10);
|
|
3325
3735
|
if (isNaN(limit) || limit < 1) {
|
|
3326
3736
|
console.error("Error: limit must be a positive number");
|
|
@@ -3331,78 +3741,126 @@ Examples:
|
|
|
3331
3741
|
fields: options.fields
|
|
3332
3742
|
});
|
|
3333
3743
|
});
|
|
3334
|
-
agents.command("info <agent_id>").description("Get detailed agent information").option(
|
|
3744
|
+
agents.command("info <agent_id>").description("Get detailed agent information").option(
|
|
3745
|
+
"--fields <fields>",
|
|
3746
|
+
"Comma-separated list of fields to return (e.g., agent_name,response_engine.type,voice_config)"
|
|
3747
|
+
).addHelpText(
|
|
3748
|
+
"after",
|
|
3749
|
+
`
|
|
3335
3750
|
Examples:
|
|
3336
3751
|
$ retell agents info agent_123abc
|
|
3337
3752
|
$ retell agents info agent_123abc --fields agent_name,response_engine.type
|
|
3338
3753
|
$ retell agents info agent_123abc | jq '.response_engine.type'
|
|
3339
|
-
`
|
|
3754
|
+
`
|
|
3755
|
+
).action(async (agentId, options) => {
|
|
3340
3756
|
await agentInfoCommand(agentId, {
|
|
3341
3757
|
fields: options.fields
|
|
3342
3758
|
});
|
|
3343
3759
|
});
|
|
3344
3760
|
var prompts = program.command("prompts").description("Manage agent prompts");
|
|
3345
|
-
prompts.command("pull <agent_id>").description("Download agent prompts to a local file").option(
|
|
3761
|
+
prompts.command("pull <agent_id>").description("Download agent prompts to a local file").option(
|
|
3762
|
+
"-o, --output <path>",
|
|
3763
|
+
"Output file path (default: .retell-prompts/<agent_id>.json)",
|
|
3764
|
+
".retell-prompts"
|
|
3765
|
+
).addHelpText(
|
|
3766
|
+
"after",
|
|
3767
|
+
`
|
|
3346
3768
|
Examples:
|
|
3347
3769
|
$ retell prompts pull agent_123abc
|
|
3348
3770
|
$ retell prompts pull agent_123abc --output my-prompts.json
|
|
3349
|
-
`
|
|
3771
|
+
`
|
|
3772
|
+
).action(async (agentId, options) => {
|
|
3350
3773
|
await pullPromptsCommand(agentId, options);
|
|
3351
3774
|
});
|
|
3352
|
-
prompts.command("diff <agent_id>").description("Show differences between local and remote prompts").option(
|
|
3775
|
+
prompts.command("diff <agent_id>").description("Show differences between local and remote prompts").option(
|
|
3776
|
+
"-s, --source <path>",
|
|
3777
|
+
"Source directory path (default: .retell-prompts)",
|
|
3778
|
+
".retell-prompts"
|
|
3779
|
+
).option("-f, --fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
3780
|
+
"after",
|
|
3781
|
+
`
|
|
3353
3782
|
Examples:
|
|
3354
3783
|
$ retell prompts diff agent_123abc
|
|
3355
3784
|
$ retell prompts diff agent_123abc --source ./custom-prompts
|
|
3356
3785
|
$ retell prompts diff agent_123abc --fields has_changes,changes.general_prompt
|
|
3357
|
-
`
|
|
3786
|
+
`
|
|
3787
|
+
).action(async (agentId, options) => {
|
|
3358
3788
|
await diffPromptsCommand(agentId, options);
|
|
3359
3789
|
});
|
|
3360
|
-
prompts.command("update <agent_id>").description("Update agent prompts from a local file").option(
|
|
3790
|
+
prompts.command("update <agent_id>").description("Update agent prompts from a local file").option(
|
|
3791
|
+
"-s, --source <path>",
|
|
3792
|
+
"Source file path (default: .retell-prompts/<agent_id>.json)",
|
|
3793
|
+
".retell-prompts"
|
|
3794
|
+
).option("--dry-run", "Preview changes without applying them", false).addHelpText(
|
|
3795
|
+
"after",
|
|
3796
|
+
`
|
|
3361
3797
|
Examples:
|
|
3362
3798
|
$ retell prompts update agent_123abc --source my-prompts.json --dry-run
|
|
3363
3799
|
$ retell prompts update agent_123abc --source my-prompts.json
|
|
3364
3800
|
# Remember to publish: retell agent-publish agent_123abc
|
|
3365
|
-
`
|
|
3801
|
+
`
|
|
3802
|
+
).action(async (agentId, options) => {
|
|
3366
3803
|
await updatePromptsCommand(agentId, options);
|
|
3367
3804
|
});
|
|
3368
|
-
program.command("agent-publish <agent_id>").description("Publish a draft agent to make changes live").addHelpText(
|
|
3805
|
+
program.command("agent-publish <agent_id>").description("Publish a draft agent to make changes live").addHelpText(
|
|
3806
|
+
"after",
|
|
3807
|
+
`
|
|
3369
3808
|
Examples:
|
|
3370
3809
|
$ retell agent-publish agent_123abc
|
|
3371
3810
|
# Run this after updating prompts to make changes live
|
|
3372
|
-
`
|
|
3811
|
+
`
|
|
3812
|
+
).action(async (agentId) => {
|
|
3373
3813
|
await publishAgentCommand(agentId);
|
|
3374
3814
|
});
|
|
3375
3815
|
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(
|
|
3816
|
+
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(
|
|
3817
|
+
"after",
|
|
3818
|
+
`
|
|
3377
3819
|
Examples:
|
|
3378
3820
|
$ retell tools list agent_123abc
|
|
3379
3821
|
$ retell tools list agent_123abc --state greeting
|
|
3380
3822
|
$ retell tools list agent_123abc --fields total_count,general_tools
|
|
3381
|
-
`
|
|
3823
|
+
`
|
|
3824
|
+
).action(async (agentId, options) => {
|
|
3382
3825
|
await listToolsCommand(agentId, {
|
|
3383
3826
|
state: options.state,
|
|
3384
3827
|
component: options.component,
|
|
3385
3828
|
fields: options.fields
|
|
3386
3829
|
});
|
|
3387
3830
|
});
|
|
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(
|
|
3831
|
+
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(
|
|
3832
|
+
"--component <id>",
|
|
3833
|
+
"Component ID to search within (Conversation Flow only)"
|
|
3834
|
+
).option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
3835
|
+
"after",
|
|
3836
|
+
`
|
|
3389
3837
|
Examples:
|
|
3390
3838
|
$ retell tools get agent_123abc lookup_customer
|
|
3391
3839
|
$ retell tools get agent_123abc book_cal --state booking
|
|
3392
3840
|
$ retell tools get agent_123abc my_tool --fields tool.name,tool.type
|
|
3393
|
-
`
|
|
3841
|
+
`
|
|
3842
|
+
).action(async (agentId, toolName, options) => {
|
|
3394
3843
|
await getToolCommand(agentId, toolName, {
|
|
3395
3844
|
state: options.state,
|
|
3396
3845
|
component: options.component,
|
|
3397
3846
|
fields: options.fields
|
|
3398
3847
|
});
|
|
3399
3848
|
});
|
|
3400
|
-
tools.command("add <agent_id>").description("Add a new tool to an agent").requiredOption(
|
|
3849
|
+
tools.command("add <agent_id>").description("Add a new tool to an agent").requiredOption(
|
|
3850
|
+
"-f, --file <path>",
|
|
3851
|
+
"Path to JSON file containing tool definition"
|
|
3852
|
+
).option("--state <name>", "Add to specific state (Retell LLM only)").option(
|
|
3853
|
+
"--component <id>",
|
|
3854
|
+
"Add to specific component (Conversation Flow only)"
|
|
3855
|
+
).option("--dry-run", "Preview changes without applying them").addHelpText(
|
|
3856
|
+
"after",
|
|
3857
|
+
`
|
|
3401
3858
|
Examples:
|
|
3402
3859
|
$ retell tools add agent_123abc --file tool.json
|
|
3403
3860
|
$ retell tools add agent_123abc --file tool.json --state booking
|
|
3404
3861
|
$ retell tools add agent_123abc --file tool.json --dry-run
|
|
3405
|
-
`
|
|
3862
|
+
`
|
|
3863
|
+
).action(async (agentId, options) => {
|
|
3406
3864
|
await addToolCommand(agentId, {
|
|
3407
3865
|
file: options.file,
|
|
3408
3866
|
state: options.state,
|
|
@@ -3410,12 +3868,21 @@ Examples:
|
|
|
3410
3868
|
dryRun: options.dryRun
|
|
3411
3869
|
});
|
|
3412
3870
|
});
|
|
3413
|
-
tools.command("update <agent_id> <tool_name>").description("Update an existing tool").requiredOption(
|
|
3871
|
+
tools.command("update <agent_id> <tool_name>").description("Update an existing tool").requiredOption(
|
|
3872
|
+
"-f, --file <path>",
|
|
3873
|
+
"Path to JSON file containing updated tool definition"
|
|
3874
|
+
).option("--state <name>", "State where tool exists (Retell LLM only)").option(
|
|
3875
|
+
"--component <id>",
|
|
3876
|
+
"Component where tool exists (Conversation Flow only)"
|
|
3877
|
+
).option("--dry-run", "Preview changes without applying them").addHelpText(
|
|
3878
|
+
"after",
|
|
3879
|
+
`
|
|
3414
3880
|
Examples:
|
|
3415
3881
|
$ retell tools update agent_123abc lookup_customer --file tool.json
|
|
3416
3882
|
$ retell tools update agent_123abc book_cal --file tool.json --state booking
|
|
3417
3883
|
$ retell tools update agent_123abc my_tool --file tool.json --dry-run
|
|
3418
|
-
`
|
|
3884
|
+
`
|
|
3885
|
+
).action(async (agentId, toolName, options) => {
|
|
3419
3886
|
await updateToolCommand(agentId, toolName, {
|
|
3420
3887
|
file: options.file,
|
|
3421
3888
|
state: options.state,
|
|
@@ -3423,34 +3890,55 @@ Examples:
|
|
|
3423
3890
|
dryRun: options.dryRun
|
|
3424
3891
|
});
|
|
3425
3892
|
});
|
|
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(
|
|
3893
|
+
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(
|
|
3894
|
+
"--component <id>",
|
|
3895
|
+
"Component where tool exists (Conversation Flow only)"
|
|
3896
|
+
).option("--dry-run", "Preview changes without applying them").addHelpText(
|
|
3897
|
+
"after",
|
|
3898
|
+
`
|
|
3427
3899
|
Examples:
|
|
3428
3900
|
$ retell tools remove agent_123abc lookup_customer
|
|
3429
3901
|
$ retell tools remove agent_123abc book_cal --state booking
|
|
3430
3902
|
$ retell tools remove agent_123abc my_tool --dry-run
|
|
3431
|
-
`
|
|
3903
|
+
`
|
|
3904
|
+
).action(async (agentId, toolName, options) => {
|
|
3432
3905
|
await removeToolCommand(agentId, toolName, {
|
|
3433
3906
|
state: options.state,
|
|
3434
3907
|
component: options.component,
|
|
3435
3908
|
dryRun: options.dryRun
|
|
3436
3909
|
});
|
|
3437
3910
|
});
|
|
3438
|
-
tools.command("export <agent_id>").description("Export all tools from an agent to a JSON file").option(
|
|
3911
|
+
tools.command("export <agent_id>").description("Export all tools from an agent to a JSON file").option(
|
|
3912
|
+
"-o, --output <path>",
|
|
3913
|
+
"Output file path (prints to stdout if not specified)"
|
|
3914
|
+
).addHelpText(
|
|
3915
|
+
"after",
|
|
3916
|
+
`
|
|
3439
3917
|
Examples:
|
|
3440
3918
|
$ retell tools export agent_123abc
|
|
3441
3919
|
$ retell tools export agent_123abc --output tools.json
|
|
3442
3920
|
$ retell tools export agent_123abc > tools.json
|
|
3443
|
-
`
|
|
3921
|
+
`
|
|
3922
|
+
).action(async (agentId, options) => {
|
|
3444
3923
|
await exportToolsCommand(agentId, {
|
|
3445
3924
|
output: options.output
|
|
3446
3925
|
});
|
|
3447
3926
|
});
|
|
3448
|
-
tools.command("import <agent_id>").description("Import tools from a JSON file to an agent").requiredOption(
|
|
3927
|
+
tools.command("import <agent_id>").description("Import tools from a JSON file to an agent").requiredOption(
|
|
3928
|
+
"-f, --file <path>",
|
|
3929
|
+
"Path to JSON file containing tools to import"
|
|
3930
|
+
).option("--dry-run", "Preview changes without applying them").option(
|
|
3931
|
+
"--replace",
|
|
3932
|
+
"Replace existing tools with same name instead of skipping"
|
|
3933
|
+
).addHelpText(
|
|
3934
|
+
"after",
|
|
3935
|
+
`
|
|
3449
3936
|
Examples:
|
|
3450
3937
|
$ retell tools import agent_123abc --file tools.json
|
|
3451
3938
|
$ retell tools import agent_123abc --file tools.json --dry-run
|
|
3452
3939
|
$ retell tools import agent_123abc --file tools.json --replace
|
|
3453
|
-
`
|
|
3940
|
+
`
|
|
3941
|
+
).action(async (agentId, options) => {
|
|
3454
3942
|
await importToolsCommand(agentId, {
|
|
3455
3943
|
file: options.file,
|
|
3456
3944
|
dryRun: options.dryRun,
|
|
@@ -3459,12 +3947,18 @@ Examples:
|
|
|
3459
3947
|
});
|
|
3460
3948
|
var tests = program.command("tests").description("Manage test cases, batch tests, and test runs");
|
|
3461
3949
|
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(
|
|
3950
|
+
testsCases.command("list").description("List all test case definitions for an LLM or flow").requiredOption(
|
|
3951
|
+
"-t, --type <type>",
|
|
3952
|
+
"Response engine type (retell-llm or conversation-flow)"
|
|
3953
|
+
).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(
|
|
3954
|
+
"after",
|
|
3955
|
+
`
|
|
3463
3956
|
Examples:
|
|
3464
3957
|
$ retell tests cases list --type retell-llm --llm-id llm_abc123
|
|
3465
3958
|
$ retell tests cases list --type conversation-flow --flow-id cf_abc123
|
|
3466
3959
|
$ retell tests cases list --type retell-llm --llm-id llm_abc123 --fields test_case_definitions
|
|
3467
|
-
`
|
|
3960
|
+
`
|
|
3961
|
+
).action(async (options) => {
|
|
3468
3962
|
if (options.type !== "retell-llm" && options.type !== "conversation-flow") {
|
|
3469
3963
|
console.error('Error: type must be "retell-llm" or "conversation-flow"');
|
|
3470
3964
|
process.exit(1);
|
|
@@ -3476,16 +3970,24 @@ Examples:
|
|
|
3476
3970
|
fields: options.fields
|
|
3477
3971
|
});
|
|
3478
3972
|
});
|
|
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(
|
|
3973
|
+
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(
|
|
3974
|
+
"after",
|
|
3975
|
+
`
|
|
3480
3976
|
Examples:
|
|
3481
3977
|
$ retell tests cases get tcd_abc123
|
|
3482
3978
|
$ retell tests cases get tcd_abc123 --fields name,user_prompt
|
|
3483
|
-
`
|
|
3979
|
+
`
|
|
3980
|
+
).action(async (testCaseDefinitionId, options) => {
|
|
3484
3981
|
await getTestCaseCommand(testCaseDefinitionId, {
|
|
3485
3982
|
fields: options.fields
|
|
3486
3983
|
});
|
|
3487
3984
|
});
|
|
3488
|
-
testsCases.command("create").description("Create a new test case definition from a JSON file").requiredOption(
|
|
3985
|
+
testsCases.command("create").description("Create a new test case definition from a JSON file").requiredOption(
|
|
3986
|
+
"-f, --file <path>",
|
|
3987
|
+
"Path to JSON file containing test case definition"
|
|
3988
|
+
).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(
|
|
3989
|
+
"after",
|
|
3990
|
+
`
|
|
3489
3991
|
Examples:
|
|
3490
3992
|
$ retell tests cases create --file test-case.json --llm-id llm_abc123
|
|
3491
3993
|
$ retell tests cases create --file test-case.json --flow-id cf_abc123
|
|
@@ -3498,7 +4000,8 @@ Test case JSON format:
|
|
|
3498
4000
|
"scenario": "User is calling about an order issue",
|
|
3499
4001
|
"metrics": ["response_quality", "task_completion"]
|
|
3500
4002
|
}
|
|
3501
|
-
`
|
|
4003
|
+
`
|
|
4004
|
+
).action(async (options) => {
|
|
3502
4005
|
await createTestCaseCommand({
|
|
3503
4006
|
file: options.file,
|
|
3504
4007
|
llmId: options.llmId,
|
|
@@ -3506,27 +4009,42 @@ Test case JSON format:
|
|
|
3506
4009
|
version: options.version ? parseInt(options.version, 10) : void 0
|
|
3507
4010
|
});
|
|
3508
4011
|
});
|
|
3509
|
-
testsCases.command("update <test_case_definition_id>").description("Update an existing test case definition from a JSON file").requiredOption(
|
|
4012
|
+
testsCases.command("update <test_case_definition_id>").description("Update an existing test case definition from a JSON file").requiredOption(
|
|
4013
|
+
"-f, --file <path>",
|
|
4014
|
+
"Path to JSON file containing test case updates"
|
|
4015
|
+
).addHelpText(
|
|
4016
|
+
"after",
|
|
4017
|
+
`
|
|
3510
4018
|
Examples:
|
|
3511
4019
|
$ retell tests cases update tcd_abc123 --file test-case.json
|
|
3512
|
-
`
|
|
4020
|
+
`
|
|
4021
|
+
).action(async (testCaseDefinitionId, options) => {
|
|
3513
4022
|
await updateTestCaseCommand(testCaseDefinitionId, {
|
|
3514
4023
|
file: options.file
|
|
3515
4024
|
});
|
|
3516
4025
|
});
|
|
3517
|
-
testsCases.command("delete <test_case_definition_id>").description("Delete a test case definition").addHelpText(
|
|
4026
|
+
testsCases.command("delete <test_case_definition_id>").description("Delete a test case definition").addHelpText(
|
|
4027
|
+
"after",
|
|
4028
|
+
`
|
|
3518
4029
|
Examples:
|
|
3519
4030
|
$ retell tests cases delete tcd_abc123
|
|
3520
|
-
`
|
|
4031
|
+
`
|
|
4032
|
+
).action(async (testCaseDefinitionId) => {
|
|
3521
4033
|
await deleteTestCaseCommand(testCaseDefinitionId);
|
|
3522
4034
|
});
|
|
3523
4035
|
var testsBatch = tests.command("batch").description("Manage batch tests");
|
|
3524
|
-
testsBatch.command("list").description("List all batch tests for an LLM or flow").requiredOption(
|
|
4036
|
+
testsBatch.command("list").description("List all batch tests for an LLM or flow").requiredOption(
|
|
4037
|
+
"-t, --type <type>",
|
|
4038
|
+
"Response engine type (retell-llm or conversation-flow)"
|
|
4039
|
+
).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(
|
|
4040
|
+
"after",
|
|
4041
|
+
`
|
|
3525
4042
|
Examples:
|
|
3526
4043
|
$ retell tests batch list --type retell-llm --llm-id llm_abc123
|
|
3527
4044
|
$ retell tests batch list --type conversation-flow --flow-id cf_abc123
|
|
3528
4045
|
$ retell tests batch list --type retell-llm --llm-id llm_abc123 --fields batch_tests
|
|
3529
|
-
`
|
|
4046
|
+
`
|
|
4047
|
+
).action(async (options) => {
|
|
3530
4048
|
if (options.type !== "retell-llm" && options.type !== "conversation-flow") {
|
|
3531
4049
|
console.error('Error: type must be "retell-llm" or "conversation-flow"');
|
|
3532
4050
|
process.exit(1);
|
|
@@ -3538,21 +4056,30 @@ Examples:
|
|
|
3538
4056
|
fields: options.fields
|
|
3539
4057
|
});
|
|
3540
4058
|
});
|
|
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(
|
|
4059
|
+
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(
|
|
4060
|
+
"after",
|
|
4061
|
+
`
|
|
3542
4062
|
Examples:
|
|
3543
4063
|
$ retell tests batch get bjj_abc123
|
|
3544
4064
|
$ retell tests batch get bjj_abc123 --fields status,stats
|
|
3545
|
-
`
|
|
4065
|
+
`
|
|
4066
|
+
).action(async (batchJobId, options) => {
|
|
3546
4067
|
await getBatchTestCommand(batchJobId, {
|
|
3547
4068
|
fields: options.fields
|
|
3548
4069
|
});
|
|
3549
4070
|
});
|
|
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(
|
|
4071
|
+
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(
|
|
4072
|
+
"--cases <ids>",
|
|
4073
|
+
"Comma-separated list of test case definition IDs"
|
|
4074
|
+
).option("--version <number>", "Version of the LLM or flow (optional)").addHelpText(
|
|
4075
|
+
"after",
|
|
4076
|
+
`
|
|
3551
4077
|
Examples:
|
|
3552
4078
|
$ retell tests batch create --llm-id llm_abc123 --cases tcd_xxx,tcd_yyy,tcd_zzz
|
|
3553
4079
|
$ retell tests batch create --flow-id cf_abc123 --cases tcd_xxx,tcd_yyy
|
|
3554
4080
|
$ retell tests batch create --llm-id llm_abc123 --cases tcd_xxx --version 2
|
|
3555
|
-
`
|
|
4081
|
+
`
|
|
4082
|
+
).action(async (options) => {
|
|
3556
4083
|
await createBatchTestCommand({
|
|
3557
4084
|
llmId: options.llmId,
|
|
3558
4085
|
flowId: options.flowId,
|
|
@@ -3561,44 +4088,64 @@ Examples:
|
|
|
3561
4088
|
});
|
|
3562
4089
|
});
|
|
3563
4090
|
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(
|
|
4091
|
+
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(
|
|
4092
|
+
"after",
|
|
4093
|
+
`
|
|
3565
4094
|
Examples:
|
|
3566
4095
|
$ retell tests runs list bjj_abc123
|
|
3567
4096
|
$ retell tests runs list bjj_abc123 --fields test_runs
|
|
3568
|
-
`
|
|
4097
|
+
`
|
|
4098
|
+
).action(async (batchJobId, options) => {
|
|
3569
4099
|
await listTestRunsCommand(batchJobId, {
|
|
3570
4100
|
fields: options.fields
|
|
3571
4101
|
});
|
|
3572
4102
|
});
|
|
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(
|
|
4103
|
+
testsRuns.command("get <test_run_id>").description("Get a specific test run result").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4104
|
+
"after",
|
|
4105
|
+
`
|
|
3574
4106
|
Examples:
|
|
3575
4107
|
$ retell tests runs get tcj_abc123
|
|
3576
4108
|
$ retell tests runs get tcj_abc123 --fields status,metric_results
|
|
3577
|
-
`
|
|
4109
|
+
`
|
|
4110
|
+
).action(async (testRunId, options) => {
|
|
3578
4111
|
await getTestRunCommand(testRunId, {
|
|
3579
4112
|
fields: options.fields
|
|
3580
4113
|
});
|
|
3581
4114
|
});
|
|
3582
4115
|
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(
|
|
4116
|
+
kb.command("list").description("List all knowledge bases").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4117
|
+
"after",
|
|
4118
|
+
`
|
|
3584
4119
|
Examples:
|
|
3585
4120
|
$ retell kb list
|
|
3586
4121
|
$ retell kb list --fields knowledge_base_id,knowledge_base_name,status
|
|
3587
|
-
`
|
|
4122
|
+
`
|
|
4123
|
+
).action(async (options) => {
|
|
3588
4124
|
await listKnowledgeBasesCommand({
|
|
3589
4125
|
fields: options.fields
|
|
3590
4126
|
});
|
|
3591
4127
|
});
|
|
3592
|
-
kb.command("get <knowledge_base_id>").description("Get a specific knowledge base").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4128
|
+
kb.command("get <knowledge_base_id>").description("Get a specific knowledge base").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4129
|
+
"after",
|
|
4130
|
+
`
|
|
3593
4131
|
Examples:
|
|
3594
4132
|
$ retell kb get kb_abc123
|
|
3595
4133
|
$ retell kb get kb_abc123 --fields knowledge_base_name,status,knowledge_base_sources
|
|
3596
|
-
`
|
|
4134
|
+
`
|
|
4135
|
+
).action(async (knowledgeBaseId, options) => {
|
|
3597
4136
|
await getKnowledgeBaseCommand(knowledgeBaseId, {
|
|
3598
4137
|
fields: options.fields
|
|
3599
4138
|
});
|
|
3600
4139
|
});
|
|
3601
|
-
kb.command("create").description("Create a new knowledge base").requiredOption(
|
|
4140
|
+
kb.command("create").description("Create a new knowledge base").requiredOption(
|
|
4141
|
+
"-n, --name <name>",
|
|
4142
|
+
"Knowledge base name (max 40 characters)"
|
|
4143
|
+
).option("--urls <urls>", "Comma-separated list of URLs to scrape").option(
|
|
4144
|
+
"--texts <file>",
|
|
4145
|
+
"Path to JSON file with text entries [{ title, text }, ...]"
|
|
4146
|
+
).option("--auto-refresh", "Enable 12-hour automatic refresh for URL sources").addHelpText(
|
|
4147
|
+
"after",
|
|
4148
|
+
`
|
|
3602
4149
|
Examples:
|
|
3603
4150
|
$ retell kb create --name "Product Docs"
|
|
3604
4151
|
$ retell kb create --name "Support KB" --urls https://docs.example.com,https://help.example.com
|
|
@@ -3610,7 +4157,8 @@ Text file format (texts.json):
|
|
|
3610
4157
|
{ "title": "Getting Started", "text": "Welcome to our product..." },
|
|
3611
4158
|
{ "title": "FAQ", "text": "Frequently asked questions..." }
|
|
3612
4159
|
]
|
|
3613
|
-
`
|
|
4160
|
+
`
|
|
4161
|
+
).action(async (options) => {
|
|
3614
4162
|
await createKnowledgeBaseCommand({
|
|
3615
4163
|
name: options.name,
|
|
3616
4164
|
urls: options.urls,
|
|
@@ -3618,40 +4166,61 @@ Text file format (texts.json):
|
|
|
3618
4166
|
autoRefresh: options.autoRefresh
|
|
3619
4167
|
});
|
|
3620
4168
|
});
|
|
3621
|
-
kb.command("delete <knowledge_base_id>").description("Delete a knowledge base").addHelpText(
|
|
4169
|
+
kb.command("delete <knowledge_base_id>").description("Delete a knowledge base").addHelpText(
|
|
4170
|
+
"after",
|
|
4171
|
+
`
|
|
3622
4172
|
Examples:
|
|
3623
4173
|
$ retell kb delete kb_abc123
|
|
3624
|
-
`
|
|
4174
|
+
`
|
|
4175
|
+
).action(async (knowledgeBaseId) => {
|
|
3625
4176
|
await deleteKnowledgeBaseCommand(knowledgeBaseId);
|
|
3626
4177
|
});
|
|
3627
4178
|
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(
|
|
4179
|
+
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(
|
|
4180
|
+
"--texts <file>",
|
|
4181
|
+
"Path to JSON file with text entries [{ title, text }, ...]"
|
|
4182
|
+
).addHelpText(
|
|
4183
|
+
"after",
|
|
4184
|
+
`
|
|
3629
4185
|
Examples:
|
|
3630
4186
|
$ retell kb sources add kb_abc123 --urls https://docs.example.com/new
|
|
3631
4187
|
$ retell kb sources add kb_abc123 --texts additional-texts.json
|
|
3632
4188
|
$ retell kb sources add kb_abc123 --urls https://faq.example.com --texts more-texts.json
|
|
3633
|
-
`
|
|
4189
|
+
`
|
|
4190
|
+
).action(async (knowledgeBaseId, options) => {
|
|
3634
4191
|
await addKnowledgeBaseSourcesCommand(knowledgeBaseId, {
|
|
3635
4192
|
urls: options.urls,
|
|
3636
4193
|
texts: options.texts
|
|
3637
4194
|
});
|
|
3638
4195
|
});
|
|
3639
|
-
kbSources.command("delete <knowledge_base_id> <source_id>").description("Remove a source from a knowledge base").addHelpText(
|
|
4196
|
+
kbSources.command("delete <knowledge_base_id> <source_id>").description("Remove a source from a knowledge base").addHelpText(
|
|
4197
|
+
"after",
|
|
4198
|
+
`
|
|
3640
4199
|
Examples:
|
|
3641
4200
|
$ retell kb sources delete kb_abc123 source_xyz789
|
|
3642
|
-
`
|
|
4201
|
+
`
|
|
4202
|
+
).action(async (knowledgeBaseId, sourceId) => {
|
|
3643
4203
|
await deleteKnowledgeBaseSourceCommand(knowledgeBaseId, sourceId);
|
|
3644
4204
|
});
|
|
3645
4205
|
var flows = program.command("flows").description("Manage conversation flow response engines");
|
|
3646
|
-
flows.command("list").description("List all conversation flows").option(
|
|
4206
|
+
flows.command("list").description("List all conversation flows").option(
|
|
4207
|
+
"-l, --limit <number>",
|
|
4208
|
+
"Maximum number of flows to return (default: 100, max: 1000)",
|
|
4209
|
+
"100"
|
|
4210
|
+
).option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4211
|
+
"after",
|
|
4212
|
+
`
|
|
3647
4213
|
Examples:
|
|
3648
4214
|
$ retell flows list
|
|
3649
4215
|
$ retell flows list --limit 50
|
|
3650
4216
|
$ retell flows list --fields conversation_flow_id,version,start_speaker
|
|
3651
|
-
`
|
|
4217
|
+
`
|
|
4218
|
+
).action(async (options) => {
|
|
3652
4219
|
const limit = parseInt(options.limit, 10);
|
|
3653
4220
|
if (isNaN(limit) || limit < 1 || limit > 1e3) {
|
|
3654
|
-
console.error(
|
|
4221
|
+
console.error(
|
|
4222
|
+
"Error: limit must be a positive number between 1 and 1000"
|
|
4223
|
+
);
|
|
3655
4224
|
process.exit(1);
|
|
3656
4225
|
}
|
|
3657
4226
|
await listFlowsCommand({
|
|
@@ -3659,18 +4228,29 @@ Examples:
|
|
|
3659
4228
|
fields: options.fields
|
|
3660
4229
|
});
|
|
3661
4230
|
});
|
|
3662
|
-
flows.command("get <conversation_flow_id>").description("Get a specific conversation flow").option(
|
|
4231
|
+
flows.command("get <conversation_flow_id>").description("Get a specific conversation flow").option(
|
|
4232
|
+
"--version <number>",
|
|
4233
|
+
"Specific version to retrieve (defaults to latest)"
|
|
4234
|
+
).option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
|
|
4235
|
+
"after",
|
|
4236
|
+
`
|
|
3663
4237
|
Examples:
|
|
3664
4238
|
$ retell flows get cf_abc123
|
|
3665
4239
|
$ retell flows get cf_abc123 --version 2
|
|
3666
4240
|
$ retell flows get cf_abc123 --fields conversation_flow_id,nodes,edges
|
|
3667
|
-
`
|
|
4241
|
+
`
|
|
4242
|
+
).action(async (conversationFlowId, options) => {
|
|
3668
4243
|
await getFlowCommand(conversationFlowId, {
|
|
3669
4244
|
version: options.version ? parseInt(options.version, 10) : void 0,
|
|
3670
4245
|
fields: options.fields
|
|
3671
4246
|
});
|
|
3672
4247
|
});
|
|
3673
|
-
flows.command("create").description("Create a new conversation flow from a JSON file").requiredOption(
|
|
4248
|
+
flows.command("create").description("Create a new conversation flow from a JSON file").requiredOption(
|
|
4249
|
+
"-f, --file <path>",
|
|
4250
|
+
"Path to JSON file containing flow configuration"
|
|
4251
|
+
).addHelpText(
|
|
4252
|
+
"after",
|
|
4253
|
+
`
|
|
3674
4254
|
Examples:
|
|
3675
4255
|
$ retell flows create --file flow.json
|
|
3676
4256
|
|
|
@@ -3681,25 +4261,38 @@ Flow JSON format (minimal):
|
|
|
3681
4261
|
"nodes": [...],
|
|
3682
4262
|
"edges": [...]
|
|
3683
4263
|
}
|
|
3684
|
-
`
|
|
4264
|
+
`
|
|
4265
|
+
).action(async (options) => {
|
|
3685
4266
|
await createFlowCommand({
|
|
3686
4267
|
file: options.file
|
|
3687
4268
|
});
|
|
3688
4269
|
});
|
|
3689
|
-
flows.command("update <conversation_flow_id>").description("Update an existing conversation flow from a JSON file").requiredOption(
|
|
4270
|
+
flows.command("update <conversation_flow_id>").description("Update an existing conversation flow from a JSON file").requiredOption(
|
|
4271
|
+
"-f, --file <path>",
|
|
4272
|
+
"Path to JSON file containing flow updates"
|
|
4273
|
+
).option(
|
|
4274
|
+
"--version <number>",
|
|
4275
|
+
"Specific version to update (defaults to latest)"
|
|
4276
|
+
).addHelpText(
|
|
4277
|
+
"after",
|
|
4278
|
+
`
|
|
3690
4279
|
Examples:
|
|
3691
4280
|
$ retell flows update cf_abc123 --file updates.json
|
|
3692
4281
|
$ retell flows update cf_abc123 --file updates.json --version 2
|
|
3693
|
-
`
|
|
4282
|
+
`
|
|
4283
|
+
).action(async (conversationFlowId, options) => {
|
|
3694
4284
|
await updateFlowCommand(conversationFlowId, {
|
|
3695
4285
|
file: options.file,
|
|
3696
4286
|
version: options.version ? parseInt(options.version, 10) : void 0
|
|
3697
4287
|
});
|
|
3698
4288
|
});
|
|
3699
|
-
flows.command("delete <conversation_flow_id>").description("Delete a conversation flow").addHelpText(
|
|
4289
|
+
flows.command("delete <conversation_flow_id>").description("Delete a conversation flow").addHelpText(
|
|
4290
|
+
"after",
|
|
4291
|
+
`
|
|
3700
4292
|
Examples:
|
|
3701
4293
|
$ retell flows delete cf_abc123
|
|
3702
|
-
`
|
|
4294
|
+
`
|
|
4295
|
+
).action(async (conversationFlowId) => {
|
|
3703
4296
|
await deleteFlowCommand(conversationFlowId);
|
|
3704
4297
|
});
|
|
3705
4298
|
program.parse(process.argv);
|