retell-cli 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,65 @@ All notable changes to the Retell AI CLI will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.4.0] - 2026-01-27
9
+
10
+ ### Added
11
+
12
+ #### Agent CRUD Commands
13
+ - **`retell agents create`** - Create new agents with configurable response engines
14
+ - `--voice` - Voice ID for the agent (required)
15
+ - `--name` - Agent name
16
+ - `--llm-id` - Retell LLM ID (creates retell-llm response engine)
17
+ - `--flow-id` - Conversation Flow ID (creates conversation-flow response engine)
18
+ - `--custom-llm` - Custom LLM WebSocket URL
19
+ - `--file` - Full agent config from JSON file
20
+ - `--fields` - Filter output fields
21
+ - Validates exactly one response engine type is specified
22
+
23
+ - **`retell agents delete <agent_id>`** - Delete an agent
24
+ - Returns success confirmation with deleted agent ID
25
+
26
+ - **`retell agents versions <agent_id>`** - List all versions of an agent
27
+ - Shows version number, publish status, agent name, and timestamp
28
+ - `--fields` - Filter output fields
29
+
30
+ #### Phone Number Commands
31
+ - **`retell phone-numbers list`** - List all phone numbers
32
+ - Shows phone number, pretty format, type, nickname, and agent IDs
33
+ - `--fields` - Filter output fields
34
+
35
+ - **`retell phone-numbers get <phone_number>`** - Get phone number details
36
+ - Retrieves full phone number configuration
37
+ - `--fields` - Filter output fields
38
+
39
+ - **`retell phone-numbers import`** - Import phone number from custom telephony
40
+ - `--number` - Phone number in E.164 format (required)
41
+ - `--termination-uri` - SIP trunk termination URI (required)
42
+ - `--nickname` - Friendly name for reference
43
+ - `--inbound-agent` - Agent ID for inbound calls
44
+ - `--outbound-agent` - Agent ID for outbound calls
45
+ - `--sip-username` - SIP trunk auth username
46
+ - `--sip-password` - SIP trunk auth password
47
+ - `--fields` - Filter output fields
48
+
49
+ #### Testing
50
+ - Added 32 new unit tests for all new commands
51
+ - 11 tests for `agents create` (validation, response engines, file loading)
52
+ - 3 tests for `agents delete` (success, error handling)
53
+ - 4 tests for `agents versions` (listing, filtering, errors)
54
+ - 4 tests for `phone-numbers list` (listing, filtering, errors)
55
+ - 4 tests for `phone-numbers get` (retrieval, filtering, errors)
56
+ - 6 tests for `phone-numbers import` (options, filtering, errors)
57
+
58
+ **Total: 138 tests passing** ✅
59
+
60
+ #### Documentation
61
+ - Updated README.md with complete command documentation
62
+ - Added examples for all new commands
63
+ - Updated features list to include phone number management
64
+
65
+ ---
66
+
8
67
  ## [Unreleased] - v1.0.1
9
68
 
10
69
  ### Added - Phase 1: Foundation & Utilities
package/README.md CHANGED
@@ -8,7 +8,8 @@ Community-built command-line tool for Retell AI - designed to give AI assistants
8
8
  ## Features
9
9
 
10
10
  - **Transcript Management** - List, retrieve, and analyze call transcripts
11
- - **Agent Management** - View and configure Retell AI agents
11
+ - **Agent Management** - Full CRUD for Retell AI agents (create, list, update, delete, versions)
12
+ - **Phone Number Management** - List, retrieve, and import phone numbers
12
13
  - **Prompt Engineering** - Pull, edit, and update agent prompts
13
14
  - **Tool Management** - Full CRUD for agent tools (webhooks, custom functions, etc.)
14
15
  - **Multi-format Support** - Works with Retell LLM and Conversation Flows
@@ -220,6 +221,55 @@ Get detailed information about a specific agent.
220
221
  retell agents info agent_123abc
221
222
  ```
222
223
 
224
+ #### `retell agents create [options]`
225
+
226
+ Create a new agent with the specified configuration.
227
+
228
+ **Options:**
229
+ - `--voice <voice_id>` - Voice ID for the agent (required)
230
+ - `--name <name>` - Agent name
231
+ - `--llm-id <id>` - Retell LLM ID (creates retell-llm response engine)
232
+ - `--flow-id <id>` - Conversation Flow ID (creates conversation-flow response engine)
233
+ - `--custom-llm <url>` - Custom LLM WebSocket URL
234
+ - `-f, --file <path>` - Full agent config from JSON file (overrides other options)
235
+ - `--fields <fields>` - Comma-separated list of fields to return
236
+
237
+ **Note:** You must specify exactly one of `--llm-id`, `--flow-id`, `--custom-llm`, or `--file`.
238
+
239
+ **Examples:**
240
+ ```bash
241
+ # Create agent with Retell LLM
242
+ retell agents create --voice 11labs-Adrian --llm-id llm_xxx --name "Support Agent"
243
+
244
+ # Create agent with Conversation Flow
245
+ retell agents create --voice 11labs-Adrian --flow-id cf_xxx
246
+
247
+ # Create agent from JSON config file
248
+ retell agents create --file agent-config.json
249
+ ```
250
+
251
+ #### `retell agents delete <agent_id>`
252
+
253
+ Delete an agent.
254
+
255
+ **Example:**
256
+ ```bash
257
+ retell agents delete agent_123abc
258
+ ```
259
+
260
+ #### `retell agents versions <agent_id> [options]`
261
+
262
+ List all versions of an agent.
263
+
264
+ **Options:**
265
+ - `--fields <fields>` - Comma-separated list of fields to return
266
+
267
+ **Example:**
268
+ ```bash
269
+ retell agents versions agent_123abc
270
+ retell agents versions agent_123abc --fields version,is_published
271
+ ```
272
+
223
273
  ### Prompts
224
274
 
225
275
  #### `retell prompts pull <agent_id> [options]`
@@ -538,6 +588,70 @@ retell tools import agent_123abc --file tools.json --replace
538
588
  retell agent-publish agent_123abc
539
589
  ```
540
590
 
591
+ ### Phone Numbers
592
+
593
+ Manage phone numbers for your Retell AI agents.
594
+
595
+ #### `retell phone-numbers list [options]`
596
+
597
+ List all phone numbers in your account.
598
+
599
+ **Options:**
600
+ - `--fields <fields>` - Comma-separated list of fields to return
601
+
602
+ **Example:**
603
+ ```bash
604
+ retell phone-numbers list
605
+ retell phone-numbers list --fields phone_number,nickname,inbound_agent_id
606
+ ```
607
+
608
+ #### `retell phone-numbers get <phone_number> [options]`
609
+
610
+ Get details of a specific phone number.
611
+
612
+ **Options:**
613
+ - `--fields <fields>` - Comma-separated list of fields to return
614
+
615
+ **Example:**
616
+ ```bash
617
+ retell phone-numbers get +14157774444
618
+ retell phone-numbers get +14157774444 --fields phone_number,inbound_agent_id
619
+ ```
620
+
621
+ #### `retell phone-numbers import [options]`
622
+
623
+ Import a phone number from custom telephony (e.g., Twilio, Vonage).
624
+
625
+ **Options:**
626
+ - `--number <number>` - Phone number in E.164 format (required)
627
+ - `--termination-uri <uri>` - SIP trunk termination URI (required)
628
+ - `--nickname <name>` - Friendly name for reference
629
+ - `--inbound-agent <id>` - Agent ID for inbound calls
630
+ - `--outbound-agent <id>` - Agent ID for outbound calls
631
+ - `--sip-username <user>` - SIP trunk auth username
632
+ - `--sip-password <pass>` - SIP trunk auth password
633
+ - `--fields <fields>` - Comma-separated list of fields to return
634
+
635
+ **Examples:**
636
+ ```bash
637
+ # Basic import
638
+ retell phone-numbers import --number +14157774444 --termination-uri someuri.pstn.twilio.com
639
+
640
+ # Import with nickname and agent assignment
641
+ retell phone-numbers import \
642
+ --number +14157774444 \
643
+ --termination-uri someuri.pstn.twilio.com \
644
+ --nickname "Support Line" \
645
+ --inbound-agent agent_123abc
646
+
647
+ # Import with SIP authentication
648
+ retell phone-numbers import \
649
+ --number +14157774444 \
650
+ --termination-uri someuri.pstn.twilio.com \
651
+ --sip-username myuser \
652
+ --sip-password mypass
653
+ ```
654
+
541
655
  ### Field Selection
542
656
 
543
657
  Reduce output size and token usage by selecting specific fields:
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/index.ts
27
27
  var import_commander = require("commander");
28
- var import_fs16 = require("fs");
28
+ var import_fs17 = require("fs");
29
29
  var import_path6 = require("path");
30
30
 
31
31
  // src/commands/login.ts
@@ -800,8 +800,128 @@ async function agentInfoCommand(agentId, options = {}) {
800
800
  }
801
801
  }
802
802
 
803
- // src/commands/prompts/pull.ts
803
+ // src/commands/agents/create.ts
804
804
  var import_fs2 = require("fs");
805
+ async function createAgentCommand(options) {
806
+ try {
807
+ const client = getRetellClient();
808
+ let config;
809
+ if (options.file) {
810
+ if (!(0, import_fs2.existsSync)(options.file)) {
811
+ outputError(`Config file not found: ${options.file}`, "FILE_NOT_FOUND");
812
+ return;
813
+ }
814
+ try {
815
+ const content = (0, import_fs2.readFileSync)(options.file, "utf-8");
816
+ config = JSON.parse(content);
817
+ } catch (error) {
818
+ if (error instanceof SyntaxError) {
819
+ outputError(
820
+ `Invalid JSON in config file: ${error.message}`,
821
+ "INVALID_JSON"
822
+ );
823
+ } else if (error instanceof Error) {
824
+ outputError(
825
+ `Error reading config file: ${error.message}`,
826
+ "FILE_ERROR"
827
+ );
828
+ }
829
+ return;
830
+ }
831
+ } else {
832
+ const engineCount = [
833
+ options.llmId,
834
+ options.flowId,
835
+ options.customLlm
836
+ ].filter(Boolean).length;
837
+ if (engineCount === 0) {
838
+ outputError(
839
+ "Must specify one of: --llm-id, --flow-id, or --custom-llm",
840
+ "MISSING_RESPONSE_ENGINE"
841
+ );
842
+ return;
843
+ }
844
+ if (engineCount > 1) {
845
+ outputError(
846
+ "Only one of --llm-id, --flow-id, or --custom-llm can be specified",
847
+ "MULTIPLE_RESPONSE_ENGINES"
848
+ );
849
+ return;
850
+ }
851
+ let responseEngine;
852
+ if (options.llmId) {
853
+ responseEngine = {
854
+ type: "retell-llm",
855
+ llm_id: options.llmId
856
+ };
857
+ } else if (options.flowId) {
858
+ responseEngine = {
859
+ type: "conversation-flow",
860
+ conversation_flow_id: options.flowId
861
+ };
862
+ } else {
863
+ responseEngine = {
864
+ type: "custom-llm",
865
+ llm_websocket_url: options.customLlm
866
+ };
867
+ }
868
+ config = {
869
+ voice_id: options.voice,
870
+ response_engine: responseEngine
871
+ };
872
+ if (options.name) {
873
+ config.agent_name = options.name;
874
+ }
875
+ }
876
+ const agent2 = await client.agent.create(config);
877
+ const output = options.fields ? filterFields(
878
+ agent2,
879
+ options.fields.split(",").map((f) => f.trim())
880
+ ) : agent2;
881
+ outputJson(output);
882
+ } catch (error) {
883
+ handleSdkError(error);
884
+ }
885
+ }
886
+
887
+ // src/commands/agents/delete.ts
888
+ async function deleteAgentCommand(agentId) {
889
+ try {
890
+ const client = getRetellClient();
891
+ await client.agent.delete(agentId);
892
+ outputJson({
893
+ message: "Agent deleted successfully",
894
+ agent_id: agentId,
895
+ operation: "delete"
896
+ });
897
+ } catch (error) {
898
+ handleSdkError(error);
899
+ }
900
+ }
901
+
902
+ // src/commands/agents/versions.ts
903
+ async function agentVersionsCommand(agentId, options = {}) {
904
+ try {
905
+ const client = getRetellClient();
906
+ const versions = await client.agent.getVersions(agentId);
907
+ const formatted = versions.map((v) => ({
908
+ version: v.version,
909
+ is_published: v.is_published,
910
+ agent_name: v.agent_name,
911
+ last_modification_timestamp: v.last_modification_timestamp
912
+ }));
913
+ const output = options.fields ? filterFields(
914
+ formatted,
915
+ options.fields.split(",").map((f) => f.trim())
916
+ ) : formatted;
917
+ outputJson(output);
918
+ } catch (error) {
919
+ handleSdkError(error);
920
+ }
921
+ }
922
+
923
+ // src/commands/prompts/pull.ts
924
+ var import_fs3 = require("fs");
805
925
  var import_path2 = require("path");
806
926
 
807
927
  // src/services/prompt-resolver.ts
@@ -864,7 +984,7 @@ async function pullPromptsCommand(agentId, options) {
864
984
  const baseDir = options.output || ".retell-prompts";
865
985
  const agentDir = (0, import_path2.join)(baseDir, agentId);
866
986
  try {
867
- (0, import_fs2.mkdirSync)(agentDir, { recursive: true });
987
+ (0, import_fs3.mkdirSync)(agentDir, { recursive: true });
868
988
  } catch (error) {
869
989
  if (error.code === "EACCES") {
870
990
  outputError(
@@ -921,26 +1041,26 @@ function saveRetellLlmPrompts(agentDir, promptSource) {
921
1041
  version: prompts2.version,
922
1042
  pulled_at: (/* @__PURE__ */ new Date()).toISOString()
923
1043
  };
924
- (0, import_fs2.writeFileSync)(
1044
+ (0, import_fs3.writeFileSync)(
925
1045
  (0, import_path2.join)(agentDir, "metadata.json"),
926
1046
  JSON.stringify(metadata, null, 2)
927
1047
  );
928
- (0, import_fs2.writeFileSync)(
1048
+ (0, import_fs3.writeFileSync)(
929
1049
  (0, import_path2.join)(agentDir, "general_prompt.md"),
930
1050
  prompts2.general_prompt || ""
931
1051
  );
932
1052
  if (prompts2.begin_message) {
933
- (0, import_fs2.writeFileSync)((0, import_path2.join)(agentDir, "begin_message.txt"), prompts2.begin_message);
1053
+ (0, import_fs3.writeFileSync)((0, import_path2.join)(agentDir, "begin_message.txt"), prompts2.begin_message);
934
1054
  }
935
1055
  if (prompts2.states && prompts2.states.length > 0) {
936
1056
  const statesDir = (0, import_path2.join)(agentDir, "states");
937
- (0, import_fs2.mkdirSync)(statesDir, { recursive: true });
1057
+ (0, import_fs3.mkdirSync)(statesDir, { recursive: true });
938
1058
  prompts2.states.forEach((state) => {
939
1059
  const filename = `${state.name}.md`;
940
1060
  const content = `# State: ${state.name}
941
1061
 
942
1062
  ${state.state_prompt}`;
943
- (0, import_fs2.writeFileSync)((0, import_path2.join)(statesDir, filename), content);
1063
+ (0, import_fs3.writeFileSync)((0, import_path2.join)(statesDir, filename), content);
944
1064
  });
945
1065
  }
946
1066
  }
@@ -953,15 +1073,15 @@ function saveConversationFlowPrompts(agentDir, promptSource) {
953
1073
  version: prompts2.version,
954
1074
  pulled_at: (/* @__PURE__ */ new Date()).toISOString()
955
1075
  };
956
- (0, import_fs2.writeFileSync)(
1076
+ (0, import_fs3.writeFileSync)(
957
1077
  (0, import_path2.join)(agentDir, "metadata.json"),
958
1078
  JSON.stringify(metadata, null, 2)
959
1079
  );
960
- (0, import_fs2.writeFileSync)(
1080
+ (0, import_fs3.writeFileSync)(
961
1081
  (0, import_path2.join)(agentDir, "global_prompt.md"),
962
1082
  prompts2.global_prompt || ""
963
1083
  );
964
- (0, import_fs2.writeFileSync)(
1084
+ (0, import_fs3.writeFileSync)(
965
1085
  (0, import_path2.join)(agentDir, "nodes.json"),
966
1086
  JSON.stringify(prompts2.nodes, null, 2)
967
1087
  );
@@ -984,27 +1104,27 @@ function getFilesCreated(type, promptSource) {
984
1104
  }
985
1105
 
986
1106
  // src/commands/prompts/update.ts
987
- var import_fs4 = require("fs");
1107
+ var import_fs5 = require("fs");
988
1108
  var import_path4 = require("path");
989
1109
 
990
1110
  // src/services/prompt-loader.ts
991
- var import_fs3 = require("fs");
1111
+ var import_fs4 = require("fs");
992
1112
  var import_path3 = require("path");
993
1113
  function loadLocalPrompts(agentId, agentDir) {
994
- if (!(0, import_fs3.existsSync)(agentDir)) {
1114
+ if (!(0, import_fs4.existsSync)(agentDir)) {
995
1115
  throw new Error(
996
1116
  `Prompts directory not found: ${agentDir}. Run 'retell prompts pull ${agentId}' first.`
997
1117
  );
998
1118
  }
999
1119
  const metadataPath = (0, import_path3.join)(agentDir, "metadata.json");
1000
- if (!(0, import_fs3.existsSync)(metadataPath)) {
1120
+ if (!(0, import_fs4.existsSync)(metadataPath)) {
1001
1121
  throw new Error(
1002
1122
  `metadata.json not found in ${agentDir}. Directory may be corrupted.`
1003
1123
  );
1004
1124
  }
1005
1125
  let metadata;
1006
1126
  try {
1007
- metadata = JSON.parse((0, import_fs3.readFileSync)(metadataPath, "utf-8"));
1127
+ metadata = JSON.parse((0, import_fs4.readFileSync)(metadataPath, "utf-8"));
1008
1128
  } catch (error) {
1009
1129
  if (error instanceof SyntaxError) {
1010
1130
  throw new Error(`Invalid JSON in metadata.json: ${error.message}`);
@@ -1034,18 +1154,18 @@ function loadLocalPrompts(agentId, agentDir) {
1034
1154
  function loadRetellLlmPrompts(agentDir) {
1035
1155
  const prompts2 = {};
1036
1156
  const generalPromptPath = (0, import_path3.join)(agentDir, "general_prompt.md");
1037
- if (!(0, import_fs3.existsSync)(generalPromptPath)) {
1157
+ if (!(0, import_fs4.existsSync)(generalPromptPath)) {
1038
1158
  throw new Error("general_prompt.md not found");
1039
1159
  }
1040
1160
  try {
1041
- prompts2.general_prompt = (0, import_fs3.readFileSync)(generalPromptPath, "utf-8");
1161
+ prompts2.general_prompt = (0, import_fs4.readFileSync)(generalPromptPath, "utf-8");
1042
1162
  } catch (error) {
1043
1163
  throw new Error(`Failed to read general_prompt.md: ${error.message}`);
1044
1164
  }
1045
1165
  const beginMessagePath = (0, import_path3.join)(agentDir, "begin_message.txt");
1046
- if ((0, import_fs3.existsSync)(beginMessagePath)) {
1166
+ if ((0, import_fs4.existsSync)(beginMessagePath)) {
1047
1167
  try {
1048
- const beginMessage = (0, import_fs3.readFileSync)(beginMessagePath, "utf-8");
1168
+ const beginMessage = (0, import_fs4.readFileSync)(beginMessagePath, "utf-8");
1049
1169
  if (beginMessage) {
1050
1170
  prompts2.begin_message = beginMessage;
1051
1171
  }
@@ -1054,9 +1174,9 @@ function loadRetellLlmPrompts(agentDir) {
1054
1174
  }
1055
1175
  }
1056
1176
  const statesDir = (0, import_path3.join)(agentDir, "states");
1057
- if ((0, import_fs3.existsSync)(statesDir)) {
1177
+ if ((0, import_fs4.existsSync)(statesDir)) {
1058
1178
  try {
1059
- const stateFiles = (0, import_fs3.readdirSync)(statesDir).filter(
1179
+ const stateFiles = (0, import_fs4.readdirSync)(statesDir).filter(
1060
1180
  (f) => f.endsWith(".md")
1061
1181
  );
1062
1182
  if (stateFiles.length > 0) {
@@ -1064,7 +1184,7 @@ function loadRetellLlmPrompts(agentDir) {
1064
1184
  const stateName = file.replace(".md", "");
1065
1185
  let content;
1066
1186
  try {
1067
- content = (0, import_fs3.readFileSync)((0, import_path3.join)(statesDir, file), "utf-8");
1187
+ content = (0, import_fs4.readFileSync)((0, import_path3.join)(statesDir, file), "utf-8");
1068
1188
  } catch (error) {
1069
1189
  throw new Error(
1070
1190
  `Failed to read state file ${file}: ${error.message}`
@@ -1096,20 +1216,20 @@ function loadRetellLlmPrompts(agentDir) {
1096
1216
  function loadConversationFlowPrompts(agentDir) {
1097
1217
  const prompts2 = {};
1098
1218
  const globalPromptPath = (0, import_path3.join)(agentDir, "global_prompt.md");
1099
- if (!(0, import_fs3.existsSync)(globalPromptPath)) {
1219
+ if (!(0, import_fs4.existsSync)(globalPromptPath)) {
1100
1220
  throw new Error("global_prompt.md not found");
1101
1221
  }
1102
1222
  try {
1103
- prompts2.global_prompt = (0, import_fs3.readFileSync)(globalPromptPath, "utf-8");
1223
+ prompts2.global_prompt = (0, import_fs4.readFileSync)(globalPromptPath, "utf-8");
1104
1224
  } catch (error) {
1105
1225
  throw new Error(`Failed to read global_prompt.md: ${error.message}`);
1106
1226
  }
1107
1227
  const nodesPath = (0, import_path3.join)(agentDir, "nodes.json");
1108
- if (!(0, import_fs3.existsSync)(nodesPath)) {
1228
+ if (!(0, import_fs4.existsSync)(nodesPath)) {
1109
1229
  throw new Error("nodes.json not found");
1110
1230
  }
1111
1231
  try {
1112
- const nodesContent = JSON.parse((0, import_fs3.readFileSync)(nodesPath, "utf-8"));
1232
+ const nodesContent = JSON.parse((0, import_fs4.readFileSync)(nodesPath, "utf-8"));
1113
1233
  if (!Array.isArray(nodesContent)) {
1114
1234
  throw new Error("nodes.json must contain an array");
1115
1235
  }
@@ -1313,7 +1433,7 @@ async function updatePromptsCommand(agentId, options) {
1313
1433
  validateAgentId2(agentId);
1314
1434
  const baseDir = options.source || ".retell-prompts";
1315
1435
  const agentDir = (0, import_path4.join)(baseDir, agentId);
1316
- if (!(0, import_fs4.existsSync)(agentDir)) {
1436
+ if (!(0, import_fs5.existsSync)(agentDir)) {
1317
1437
  outputError(
1318
1438
  `Prompts directory not found: ${agentDir}. Run 'retell prompts pull ${agentId}' first.`,
1319
1439
  "DIRECTORY_NOT_FOUND"
@@ -1321,7 +1441,7 @@ async function updatePromptsCommand(agentId, options) {
1321
1441
  return;
1322
1442
  }
1323
1443
  const metadataPath = (0, import_path4.join)(agentDir, "metadata.json");
1324
- if (!(0, import_fs4.existsSync)(metadataPath)) {
1444
+ if (!(0, import_fs5.existsSync)(metadataPath)) {
1325
1445
  outputError(
1326
1446
  `metadata.json not found in ${agentDir}. Directory may be corrupted.`,
1327
1447
  "METADATA_NOT_FOUND"
@@ -1329,7 +1449,7 @@ async function updatePromptsCommand(agentId, options) {
1329
1449
  return;
1330
1450
  }
1331
1451
  const metadata = JSON.parse(
1332
- (0, import_fs4.readFileSync)(metadataPath, "utf-8")
1452
+ (0, import_fs5.readFileSync)(metadataPath, "utf-8")
1333
1453
  );
1334
1454
  const promptSource = await resolvePromptSource(agentId);
1335
1455
  if (promptSource.type === "custom-llm") {
@@ -1488,7 +1608,7 @@ async function getAgentCommand(agentId, options) {
1488
1608
  }
1489
1609
 
1490
1610
  // src/commands/agent/update.ts
1491
- var import_fs5 = require("fs");
1611
+ var import_fs6 = require("fs");
1492
1612
  function validateFilePath(filePath) {
1493
1613
  if (filePath.includes("\0")) {
1494
1614
  throw new Error("Invalid file path: contains null bytes");
@@ -1498,13 +1618,13 @@ async function updateAgentCommand(agentId, options) {
1498
1618
  const client = getRetellClient();
1499
1619
  try {
1500
1620
  validateFilePath(options.file);
1501
- if (!(0, import_fs5.existsSync)(options.file)) {
1621
+ if (!(0, import_fs6.existsSync)(options.file)) {
1502
1622
  outputError(`File not found: ${options.file}`, "FILE_NOT_FOUND");
1503
1623
  return;
1504
1624
  }
1505
1625
  let updateParams;
1506
1626
  try {
1507
- const fileContent = (0, import_fs5.readFileSync)(options.file, "utf-8");
1627
+ const fileContent = (0, import_fs6.readFileSync)(options.file, "utf-8");
1508
1628
  updateParams = JSON.parse(fileContent);
1509
1629
  } catch (error) {
1510
1630
  if (error instanceof SyntaxError) {
@@ -1893,16 +2013,16 @@ async function getToolCommand(agentId, toolName, options) {
1893
2013
  }
1894
2014
 
1895
2015
  // src/commands/tools/add.ts
1896
- var import_fs6 = require("fs");
2016
+ var import_fs7 = require("fs");
1897
2017
  async function addToolCommand(agentId, options) {
1898
2018
  try {
1899
- if (!(0, import_fs6.existsSync)(options.file)) {
2019
+ if (!(0, import_fs7.existsSync)(options.file)) {
1900
2020
  outputError(`Tool file not found: ${options.file}`, "FILE_NOT_FOUND");
1901
2021
  return;
1902
2022
  }
1903
2023
  let tool;
1904
2024
  try {
1905
- const content = (0, import_fs6.readFileSync)(options.file, "utf-8");
2025
+ const content = (0, import_fs7.readFileSync)(options.file, "utf-8");
1906
2026
  tool = JSON.parse(content);
1907
2027
  } catch (error) {
1908
2028
  if (error instanceof SyntaxError) {
@@ -2077,16 +2197,16 @@ async function addToolCommand(agentId, options) {
2077
2197
  }
2078
2198
 
2079
2199
  // src/commands/tools/update.ts
2080
- var import_fs7 = require("fs");
2200
+ var import_fs8 = require("fs");
2081
2201
  async function updateToolCommand(agentId, toolName, options) {
2082
2202
  try {
2083
- if (!(0, import_fs7.existsSync)(options.file)) {
2203
+ if (!(0, import_fs8.existsSync)(options.file)) {
2084
2204
  outputError(`Tool file not found: ${options.file}`, "FILE_NOT_FOUND");
2085
2205
  return;
2086
2206
  }
2087
2207
  let newTool;
2088
2208
  try {
2089
- const content = (0, import_fs7.readFileSync)(options.file, "utf-8");
2209
+ const content = (0, import_fs8.readFileSync)(options.file, "utf-8");
2090
2210
  newTool = JSON.parse(content);
2091
2211
  } catch (error) {
2092
2212
  if (error instanceof SyntaxError) {
@@ -2470,7 +2590,7 @@ async function removeToolCommand(agentId, toolName, options) {
2470
2590
  }
2471
2591
 
2472
2592
  // src/commands/tools/export.ts
2473
- var import_fs8 = require("fs");
2593
+ var import_fs9 = require("fs");
2474
2594
  async function exportToolsCommand(agentId, options) {
2475
2595
  try {
2476
2596
  const source = await resolveToolsSource(agentId);
@@ -2518,7 +2638,7 @@ async function exportToolsCommand(agentId, options) {
2518
2638
  }
2519
2639
  if (options.output) {
2520
2640
  try {
2521
- (0, import_fs8.writeFileSync)(
2641
+ (0, import_fs9.writeFileSync)(
2522
2642
  options.output,
2523
2643
  JSON.stringify(exportData, null, 2),
2524
2644
  "utf-8"
@@ -2552,16 +2672,16 @@ async function exportToolsCommand(agentId, options) {
2552
2672
  }
2553
2673
 
2554
2674
  // src/commands/tools/import.ts
2555
- var import_fs9 = require("fs");
2675
+ var import_fs10 = require("fs");
2556
2676
  async function importToolsCommand(agentId, options) {
2557
2677
  try {
2558
- if (!(0, import_fs9.existsSync)(options.file)) {
2678
+ if (!(0, import_fs10.existsSync)(options.file)) {
2559
2679
  outputError(`Import file not found: ${options.file}`, "FILE_NOT_FOUND");
2560
2680
  return;
2561
2681
  }
2562
2682
  let importData;
2563
2683
  try {
2564
- const content = (0, import_fs9.readFileSync)(options.file, "utf-8");
2684
+ const content = (0, import_fs10.readFileSync)(options.file, "utf-8");
2565
2685
  importData = JSON.parse(content);
2566
2686
  } catch (error) {
2567
2687
  if (error instanceof SyntaxError) {
@@ -2961,7 +3081,7 @@ async function getTestCaseCommand(testCaseDefinitionId, options) {
2961
3081
  }
2962
3082
 
2963
3083
  // src/commands/tests/cases/create.ts
2964
- var import_fs10 = require("fs");
3084
+ var import_fs11 = require("fs");
2965
3085
  function buildResponseEngine2(options) {
2966
3086
  if (options.llmId && options.flowId) {
2967
3087
  outputError(
@@ -2999,7 +3119,7 @@ function buildResponseEngine2(options) {
2999
3119
  }
3000
3120
  async function createTestCaseCommand(options) {
3001
3121
  try {
3002
- if (!(0, import_fs10.existsSync)(options.file)) {
3122
+ if (!(0, import_fs11.existsSync)(options.file)) {
3003
3123
  outputError(
3004
3124
  `Test case file not found: ${options.file}`,
3005
3125
  "FILE_NOT_FOUND"
@@ -3008,7 +3128,7 @@ async function createTestCaseCommand(options) {
3008
3128
  }
3009
3129
  let input;
3010
3130
  try {
3011
- const content = (0, import_fs10.readFileSync)(options.file, "utf-8");
3131
+ const content = (0, import_fs11.readFileSync)(options.file, "utf-8");
3012
3132
  input = JSON.parse(content);
3013
3133
  } catch (error) {
3014
3134
  if (error instanceof SyntaxError) {
@@ -3058,10 +3178,10 @@ async function createTestCaseCommand(options) {
3058
3178
  }
3059
3179
 
3060
3180
  // src/commands/tests/cases/update.ts
3061
- var import_fs11 = require("fs");
3181
+ var import_fs12 = require("fs");
3062
3182
  async function updateTestCaseCommand(testCaseDefinitionId, options) {
3063
3183
  try {
3064
- if (!(0, import_fs11.existsSync)(options.file)) {
3184
+ if (!(0, import_fs12.existsSync)(options.file)) {
3065
3185
  outputError(
3066
3186
  `Test case file not found: ${options.file}`,
3067
3187
  "FILE_NOT_FOUND"
@@ -3070,7 +3190,7 @@ async function updateTestCaseCommand(testCaseDefinitionId, options) {
3070
3190
  }
3071
3191
  let input;
3072
3192
  try {
3073
- const content = (0, import_fs11.readFileSync)(options.file, "utf-8");
3193
+ const content = (0, import_fs12.readFileSync)(options.file, "utf-8");
3074
3194
  input = JSON.parse(content);
3075
3195
  } catch (error) {
3076
3196
  if (error instanceof SyntaxError) {
@@ -3327,7 +3447,7 @@ async function getKnowledgeBaseCommand(knowledgeBaseId, options) {
3327
3447
  }
3328
3448
 
3329
3449
  // src/commands/kb/create.ts
3330
- var import_fs12 = require("fs");
3450
+ var import_fs13 = require("fs");
3331
3451
  async function createKnowledgeBaseCommand(options) {
3332
3452
  try {
3333
3453
  if (options.name.length > 40) {
@@ -3350,12 +3470,12 @@ async function createKnowledgeBaseCommand(options) {
3350
3470
  }
3351
3471
  }
3352
3472
  if (options.texts) {
3353
- if (!(0, import_fs12.existsSync)(options.texts)) {
3473
+ if (!(0, import_fs13.existsSync)(options.texts)) {
3354
3474
  outputError(`Texts file not found: ${options.texts}`, "FILE_NOT_FOUND");
3355
3475
  return;
3356
3476
  }
3357
3477
  try {
3358
- const content = (0, import_fs12.readFileSync)(options.texts, "utf-8");
3478
+ const content = (0, import_fs13.readFileSync)(options.texts, "utf-8");
3359
3479
  const textsData = JSON.parse(content);
3360
3480
  if (!Array.isArray(textsData)) {
3361
3481
  outputError(
@@ -3431,7 +3551,7 @@ async function deleteKnowledgeBaseCommand(knowledgeBaseId) {
3431
3551
  }
3432
3552
 
3433
3553
  // src/commands/kb/sources/add.ts
3434
- var import_fs13 = require("fs");
3554
+ var import_fs14 = require("fs");
3435
3555
  async function addKnowledgeBaseSourcesCommand(knowledgeBaseId, options) {
3436
3556
  try {
3437
3557
  if (!options.urls && !options.texts) {
@@ -3449,12 +3569,12 @@ async function addKnowledgeBaseSourcesCommand(knowledgeBaseId, options) {
3449
3569
  }
3450
3570
  }
3451
3571
  if (options.texts) {
3452
- if (!(0, import_fs13.existsSync)(options.texts)) {
3572
+ if (!(0, import_fs14.existsSync)(options.texts)) {
3453
3573
  outputError(`Texts file not found: ${options.texts}`, "FILE_NOT_FOUND");
3454
3574
  return;
3455
3575
  }
3456
3576
  try {
3457
- const content = (0, import_fs13.readFileSync)(options.texts, "utf-8");
3577
+ const content = (0, import_fs14.readFileSync)(options.texts, "utf-8");
3458
3578
  const textsData = JSON.parse(content);
3459
3579
  if (!Array.isArray(textsData)) {
3460
3580
  outputError(
@@ -3580,16 +3700,16 @@ async function getFlowCommand(conversationFlowId, options) {
3580
3700
  }
3581
3701
 
3582
3702
  // src/commands/flows/create.ts
3583
- var import_fs14 = require("fs");
3703
+ var import_fs15 = require("fs");
3584
3704
  async function createFlowCommand(options) {
3585
3705
  try {
3586
- if (!(0, import_fs14.existsSync)(options.file)) {
3706
+ if (!(0, import_fs15.existsSync)(options.file)) {
3587
3707
  outputError(`Flow file not found: ${options.file}`, "FILE_NOT_FOUND");
3588
3708
  return;
3589
3709
  }
3590
3710
  let flowConfig;
3591
3711
  try {
3592
- const content = (0, import_fs14.readFileSync)(options.file, "utf-8");
3712
+ const content = (0, import_fs15.readFileSync)(options.file, "utf-8");
3593
3713
  flowConfig = JSON.parse(content);
3594
3714
  } catch (error) {
3595
3715
  if (error instanceof SyntaxError) {
@@ -3627,16 +3747,16 @@ async function createFlowCommand(options) {
3627
3747
  }
3628
3748
 
3629
3749
  // src/commands/flows/update.ts
3630
- var import_fs15 = require("fs");
3750
+ var import_fs16 = require("fs");
3631
3751
  async function updateFlowCommand(conversationFlowId, options) {
3632
3752
  try {
3633
- if (!(0, import_fs15.existsSync)(options.file)) {
3753
+ if (!(0, import_fs16.existsSync)(options.file)) {
3634
3754
  outputError(`Flow file not found: ${options.file}`, "FILE_NOT_FOUND");
3635
3755
  return;
3636
3756
  }
3637
3757
  let flowUpdates;
3638
3758
  try {
3639
- const content = (0, import_fs15.readFileSync)(options.file, "utf-8");
3759
+ const content = (0, import_fs16.readFileSync)(options.file, "utf-8");
3640
3760
  flowUpdates = JSON.parse(content);
3641
3761
  } catch (error) {
3642
3762
  if (error instanceof SyntaxError) {
@@ -3688,9 +3808,81 @@ async function deleteFlowCommand(conversationFlowId) {
3688
3808
  }
3689
3809
  }
3690
3810
 
3811
+ // src/commands/phone-numbers/list.ts
3812
+ async function listPhoneNumbersCommand(options = {}) {
3813
+ try {
3814
+ const client = getRetellClient();
3815
+ const phoneNumbers2 = await client.phoneNumber.list();
3816
+ const formatted = phoneNumbers2.map((pn) => ({
3817
+ phone_number: pn.phone_number,
3818
+ phone_number_pretty: pn.phone_number_pretty,
3819
+ phone_number_type: pn.phone_number_type,
3820
+ nickname: pn.nickname,
3821
+ inbound_agent_id: pn.inbound_agent_id,
3822
+ outbound_agent_id: pn.outbound_agent_id
3823
+ }));
3824
+ const output = options.fields ? filterFields(
3825
+ formatted,
3826
+ options.fields.split(",").map((f) => f.trim())
3827
+ ) : formatted;
3828
+ outputJson(output);
3829
+ } catch (error) {
3830
+ handleSdkError(error);
3831
+ }
3832
+ }
3833
+
3834
+ // src/commands/phone-numbers/get.ts
3835
+ async function getPhoneNumberCommand(phoneNumber, options = {}) {
3836
+ try {
3837
+ const client = getRetellClient();
3838
+ const pn = await client.phoneNumber.retrieve(phoneNumber);
3839
+ const output = options.fields ? filterFields(
3840
+ pn,
3841
+ options.fields.split(",").map((f) => f.trim())
3842
+ ) : pn;
3843
+ outputJson(output);
3844
+ } catch (error) {
3845
+ handleSdkError(error);
3846
+ }
3847
+ }
3848
+
3849
+ // src/commands/phone-numbers/import.ts
3850
+ async function importPhoneNumberCommand(options) {
3851
+ try {
3852
+ const client = getRetellClient();
3853
+ const importParams = {
3854
+ phone_number: options.number,
3855
+ termination_uri: options.terminationUri
3856
+ };
3857
+ if (options.nickname) {
3858
+ importParams.nickname = options.nickname;
3859
+ }
3860
+ if (options.inboundAgent) {
3861
+ importParams.inbound_agent_id = options.inboundAgent;
3862
+ }
3863
+ if (options.outboundAgent) {
3864
+ importParams.outbound_agent_id = options.outboundAgent;
3865
+ }
3866
+ if (options.sipUsername) {
3867
+ importParams.sip_trunk_auth_username = options.sipUsername;
3868
+ }
3869
+ if (options.sipPassword) {
3870
+ importParams.sip_trunk_auth_password = options.sipPassword;
3871
+ }
3872
+ const pn = await client.phoneNumber.import(importParams);
3873
+ const output = options.fields ? filterFields(
3874
+ pn,
3875
+ options.fields.split(",").map((f) => f.trim())
3876
+ ) : pn;
3877
+ outputJson(output);
3878
+ } catch (error) {
3879
+ handleSdkError(error);
3880
+ }
3881
+ }
3882
+
3691
3883
  // src/index.ts
3692
3884
  var packageJson = JSON.parse(
3693
- (0, import_fs16.readFileSync)((0, import_path6.join)(__dirname, "../package.json"), "utf-8")
3885
+ (0, import_fs17.readFileSync)((0, import_path6.join)(__dirname, "../package.json"), "utf-8")
3694
3886
  );
3695
3887
  var program = new import_commander.Command();
3696
3888
  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);
@@ -3860,6 +4052,42 @@ Examples:
3860
4052
  fields: options.fields
3861
4053
  });
3862
4054
  });
4055
+ agents.command("create").description("Create a new agent").requiredOption("--voice <voice_id>", "Voice ID for the agent").option("--name <name>", "Agent name").option("--llm-id <id>", "Retell LLM ID (creates retell-llm response engine)").option(
4056
+ "--flow-id <id>",
4057
+ "Conversation Flow ID (creates conversation-flow response engine)"
4058
+ ).option("--custom-llm <url>", "Custom LLM WebSocket URL").option(
4059
+ "-f, --file <path>",
4060
+ "Full agent config from JSON file (overrides other options)"
4061
+ ).option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
4062
+ "after",
4063
+ `
4064
+ Examples:
4065
+ $ retell agents create --voice 11labs-Adrian --llm-id llm_xxx --name "Test Agent"
4066
+ $ retell agents create --voice 11labs-Adrian --flow-id cf_xxx
4067
+ $ retell agents create --file agent-config.json
4068
+ `
4069
+ ).action(async (options) => {
4070
+ await createAgentCommand(options);
4071
+ });
4072
+ agents.command("delete <agent_id>").description("Delete an agent").addHelpText(
4073
+ "after",
4074
+ `
4075
+ Examples:
4076
+ $ retell agents delete agent_123abc
4077
+ `
4078
+ ).action(async (agentId) => {
4079
+ await deleteAgentCommand(agentId);
4080
+ });
4081
+ agents.command("versions <agent_id>").description("List all versions of an agent").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
4082
+ "after",
4083
+ `
4084
+ Examples:
4085
+ $ retell agents versions agent_123abc
4086
+ $ retell agents versions agent_123abc --fields version,is_published
4087
+ `
4088
+ ).action(async (agentId, options) => {
4089
+ await agentVersionsCommand(agentId, options);
4090
+ });
3863
4091
  var prompts = program.command("prompts").description("Manage agent prompts");
3864
4092
  prompts.command("pull <agent_id>").description("Download agent prompts to a local file").option(
3865
4093
  "-o, --output <path>",
@@ -4460,6 +4688,38 @@ Examples:
4460
4688
  ).action(async (conversationFlowId) => {
4461
4689
  await deleteFlowCommand(conversationFlowId);
4462
4690
  });
4691
+ var phoneNumbers = program.command("phone-numbers").description("Manage phone numbers");
4692
+ phoneNumbers.command("list").description("List all phone numbers").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
4693
+ "after",
4694
+ `
4695
+ Examples:
4696
+ $ retell phone-numbers list
4697
+ $ retell phone-numbers list --fields phone_number,nickname,inbound_agent_id
4698
+ `
4699
+ ).action(async (options) => {
4700
+ await listPhoneNumbersCommand(options);
4701
+ });
4702
+ phoneNumbers.command("get <phone_number>").description("Get phone number details").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
4703
+ "after",
4704
+ `
4705
+ Examples:
4706
+ $ retell phone-numbers get +14157774444
4707
+ $ retell phone-numbers get +14157774444 --fields phone_number,inbound_agent_id
4708
+ `
4709
+ ).action(async (phoneNumber, options) => {
4710
+ await getPhoneNumberCommand(phoneNumber, options);
4711
+ });
4712
+ phoneNumbers.command("import").description("Import a phone number from custom telephony").requiredOption("--number <number>", "Phone number in E.164 format").requiredOption("--termination-uri <uri>", "SIP trunk termination URI").option("--nickname <name>", "Friendly name for reference").option("--inbound-agent <id>", "Agent ID for inbound calls").option("--outbound-agent <id>", "Agent ID for outbound calls").option("--sip-username <user>", "SIP trunk auth username").option("--sip-password <pass>", "SIP trunk auth password").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
4713
+ "after",
4714
+ `
4715
+ Examples:
4716
+ $ retell phone-numbers import --number +14157774444 --termination-uri someuri.pstn.twilio.com
4717
+ $ retell phone-numbers import --number +14157774444 --termination-uri someuri.pstn.twilio.com --nickname "Support Line"
4718
+ $ retell phone-numbers import --number +14157774444 --termination-uri someuri.pstn.twilio.com --inbound-agent agent_xxx
4719
+ `
4720
+ ).action(async (options) => {
4721
+ await importPhoneNumberCommand(options);
4722
+ });
4463
4723
  program.parse(process.argv);
4464
4724
  if (!process.argv.slice(2).length) {
4465
4725
  program.outputHelp();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "retell-cli",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Community CLI for Retell AI - efficient access to transcripts, agents, and prompts for AI assistants without MCP overhead",
5
5
  "main": "dist/index.js",
6
6
  "bin": {