retell-cli 1.2.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]`
@@ -305,6 +355,81 @@ Publish a draft agent to make changes live.
305
355
  retell agent-publish agent_123abc
306
356
  ```
307
357
 
358
+ ### Agent Configuration
359
+
360
+ Manage agent-level settings that aren't part of prompts (voice, webhooks, post-call analysis, etc.).
361
+
362
+ #### `retell agent get <agent_id> [options]`
363
+
364
+ Get agent configuration including all agent-level settings.
365
+
366
+ **Options:**
367
+ - `--version <number>` - Specific version to retrieve (defaults to latest)
368
+ - `--fields <fields>` - Comma-separated list of fields to return
369
+
370
+ **Examples:**
371
+ ```bash
372
+ # Get full agent config
373
+ retell agent get agent_123abc
374
+
375
+ # Get specific version
376
+ retell agent get agent_123abc --version 2
377
+
378
+ # Get specific fields only
379
+ retell agent get agent_123abc --fields agent_name,post_call_analysis_data
380
+
381
+ # Save config to file for editing
382
+ retell agent get agent_123abc > config.json
383
+ ```
384
+
385
+ #### `retell agent update <agent_id> [options]`
386
+
387
+ Update agent configuration from a JSON file. This is useful for updating agent-level fields that aren't accessible through `prompts update`, such as:
388
+ - `post_call_analysis_data` - Custom data extraction from calls
389
+ - `post_call_analysis_model` - Model for analysis
390
+ - `analysis_successful_prompt` - Success criteria prompt
391
+ - `analysis_summary_prompt` - Summary generation prompt
392
+ - Voice settings, language, webhooks, and more
393
+
394
+ **Options:**
395
+ - `-f, --file <path>` - Path to JSON file containing agent configuration updates (required)
396
+ - `--dry-run` - Preview changes without applying them
397
+ - `--version <number>` - Specific version to update (defaults to latest draft)
398
+
399
+ **Example JSON for post-call analysis:**
400
+ ```json
401
+ {
402
+ "post_call_analysis_model": "claude-4.5-sonnet",
403
+ "post_call_analysis_data": [
404
+ {
405
+ "name": "call_outcome",
406
+ "type": "enum",
407
+ "description": "Result of the call",
408
+ "choices": ["successful", "unsuccessful", "callback_needed"]
409
+ },
410
+ {
411
+ "name": "customer_sentiment",
412
+ "type": "string",
413
+ "description": "Overall customer sentiment"
414
+ }
415
+ ],
416
+ "analysis_successful_prompt": "Determine if the issue was resolved.",
417
+ "analysis_summary_prompt": "Summarize the call in 2 sentences."
418
+ }
419
+ ```
420
+
421
+ **Examples:**
422
+ ```bash
423
+ # Preview changes first (recommended)
424
+ retell agent update agent_123abc --file config.json --dry-run
425
+
426
+ # Apply changes
427
+ retell agent update agent_123abc --file config.json
428
+
429
+ # Remember to publish after updating
430
+ retell agent-publish agent_123abc
431
+ ```
432
+
308
433
  ### Tools
309
434
 
310
435
  Manage agent tools (custom functions, webhooks, etc.). Tools are embedded within Retell LLM and Conversation Flow configurations.
@@ -463,6 +588,70 @@ retell tools import agent_123abc --file tools.json --replace
463
588
  retell agent-publish agent_123abc
464
589
  ```
465
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
+
466
655
  ### Field Selection
467
656
 
468
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_fs15 = require("fs");
28
+ var import_fs17 = require("fs");
29
29
  var import_path6 = require("path");
30
30
 
31
31
  // src/commands/login.ts
@@ -751,27 +751,27 @@ async function listAgentsCommand(options = {}) {
751
751
  const agents2 = await client.agent.list({
752
752
  limit: options.limit || 100
753
753
  });
754
- const formatted = agents2.map((agent) => {
754
+ const formatted = agents2.map((agent2) => {
755
755
  let response_engine_id;
756
- switch (agent.response_engine.type) {
756
+ switch (agent2.response_engine.type) {
757
757
  case "retell-llm":
758
- response_engine_id = agent.response_engine.llm_id || "unknown";
758
+ response_engine_id = agent2.response_engine.llm_id || "unknown";
759
759
  break;
760
760
  case "conversation-flow":
761
- response_engine_id = agent.response_engine.conversation_flow_id || "unknown";
761
+ response_engine_id = agent2.response_engine.conversation_flow_id || "unknown";
762
762
  break;
763
763
  case "custom-llm":
764
- response_engine_id = agent.response_engine.llm_websocket_url || "unknown";
764
+ response_engine_id = agent2.response_engine.llm_websocket_url || "unknown";
765
765
  break;
766
766
  default:
767
767
  response_engine_id = "unknown";
768
768
  }
769
769
  return {
770
- agent_id: agent.agent_id,
771
- agent_name: agent.agent_name,
772
- version: agent.version,
773
- is_published: agent.is_published,
774
- response_engine_type: agent.response_engine.type,
770
+ agent_id: agent2.agent_id,
771
+ agent_name: agent2.agent_name,
772
+ version: agent2.version,
773
+ is_published: agent2.is_published,
774
+ response_engine_type: agent2.response_engine.type,
775
775
  response_engine_id
776
776
  };
777
777
  });
@@ -789,31 +789,151 @@ async function listAgentsCommand(options = {}) {
789
789
  async function agentInfoCommand(agentId, options = {}) {
790
790
  try {
791
791
  const client = getRetellClient();
792
- const agent = await client.agent.retrieve(agentId);
792
+ const agent2 = await client.agent.retrieve(agentId);
793
793
  const output = options.fields ? filterFields(
794
- agent,
794
+ agent2,
795
795
  options.fields.split(",").map((f) => f.trim())
796
- ) : agent;
796
+ ) : agent2;
797
797
  outputJson(output);
798
798
  } catch (error) {
799
799
  handleSdkError(error);
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
808
928
  async function resolvePromptSource(agentId) {
809
929
  const client = getRetellClient();
810
- const agent = await client.agent.retrieve(agentId);
811
- if (agent.response_engine.type === "retell-llm") {
812
- const llm = await client.llm.retrieve(agent.response_engine.llm_id);
930
+ const agent2 = await client.agent.retrieve(agentId);
931
+ if (agent2.response_engine.type === "retell-llm") {
932
+ const llm = await client.llm.retrieve(agent2.response_engine.llm_id);
813
933
  return {
814
934
  type: "retell-llm",
815
935
  llmId: llm.llm_id,
816
- agentName: agent.agent_name,
936
+ agentName: agent2.agent_name,
817
937
  prompts: {
818
938
  llm_id: llm.llm_id,
819
939
  version: llm.version,
@@ -823,14 +943,14 @@ async function resolvePromptSource(agentId) {
823
943
  }
824
944
  };
825
945
  }
826
- if (agent.response_engine.type === "conversation-flow") {
946
+ if (agent2.response_engine.type === "conversation-flow") {
827
947
  const flow = await client.conversationFlow.retrieve(
828
- agent.response_engine.conversation_flow_id
948
+ agent2.response_engine.conversation_flow_id
829
949
  );
830
950
  return {
831
951
  type: "conversation-flow",
832
952
  flowId: flow.conversation_flow_id,
833
- agentName: agent.agent_name,
953
+ agentName: agent2.agent_name,
834
954
  prompts: {
835
955
  conversation_flow_id: flow.conversation_flow_id,
836
956
  version: flow.version,
@@ -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") {
@@ -1454,13 +1574,13 @@ async function publishAgentCommand(agentId) {
1454
1574
  }
1455
1575
  }
1456
1576
  try {
1457
- const agent = await client.agent.retrieve(agentId);
1577
+ const agent2 = await client.agent.retrieve(agentId);
1458
1578
  outputJson({
1459
1579
  message: "Agent published successfully",
1460
- agent_id: agent.agent_id,
1461
- agent_name: agent.agent_name || "Unknown",
1462
- version: agent.version || "Unknown",
1463
- is_published: agent.is_published ?? true,
1580
+ agent_id: agent2.agent_id,
1581
+ agent_name: agent2.agent_name || "Unknown",
1582
+ version: agent2.version || "Unknown",
1583
+ is_published: agent2.is_published ?? true,
1464
1584
  note: "Draft version incremented and ready for new changes"
1465
1585
  });
1466
1586
  } catch (error) {
@@ -1468,12 +1588,109 @@ async function publishAgentCommand(agentId) {
1468
1588
  }
1469
1589
  }
1470
1590
 
1591
+ // src/commands/agent/get.ts
1592
+ async function getAgentCommand(agentId, options) {
1593
+ const client = getRetellClient();
1594
+ try {
1595
+ const agent2 = await client.agent.retrieve(agentId, {
1596
+ version: options.version
1597
+ });
1598
+ if (options.fields) {
1599
+ const fields = options.fields.split(",").map((f) => f.trim());
1600
+ const filtered = filterFields(agent2, fields);
1601
+ outputJson(filtered);
1602
+ } else {
1603
+ outputJson(agent2);
1604
+ }
1605
+ } catch (error) {
1606
+ handleSdkError(error);
1607
+ }
1608
+ }
1609
+
1610
+ // src/commands/agent/update.ts
1611
+ var import_fs6 = require("fs");
1612
+ function validateFilePath(filePath) {
1613
+ if (filePath.includes("\0")) {
1614
+ throw new Error("Invalid file path: contains null bytes");
1615
+ }
1616
+ }
1617
+ async function updateAgentCommand(agentId, options) {
1618
+ const client = getRetellClient();
1619
+ try {
1620
+ validateFilePath(options.file);
1621
+ if (!(0, import_fs6.existsSync)(options.file)) {
1622
+ outputError(`File not found: ${options.file}`, "FILE_NOT_FOUND");
1623
+ return;
1624
+ }
1625
+ let updateParams;
1626
+ try {
1627
+ const fileContent = (0, import_fs6.readFileSync)(options.file, "utf-8");
1628
+ updateParams = JSON.parse(fileContent);
1629
+ } catch (error) {
1630
+ if (error instanceof SyntaxError) {
1631
+ outputError(`Invalid JSON in file: ${error.message}`, "INVALID_JSON");
1632
+ return;
1633
+ }
1634
+ throw error;
1635
+ }
1636
+ if (typeof updateParams !== "object" || updateParams === null || Array.isArray(updateParams)) {
1637
+ outputError(
1638
+ "File must contain a JSON object with agent configuration fields",
1639
+ "INVALID_FORMAT"
1640
+ );
1641
+ return;
1642
+ }
1643
+ if (options.dryRun) {
1644
+ const currentAgent = await client.agent.retrieve(agentId, {
1645
+ version: options.version
1646
+ });
1647
+ const changes = {};
1648
+ for (const [key, newValue] of Object.entries(updateParams)) {
1649
+ const currentValue = currentAgent[key];
1650
+ if (JSON.stringify(currentValue) !== JSON.stringify(newValue)) {
1651
+ changes[key] = {
1652
+ current: currentValue,
1653
+ new: newValue
1654
+ };
1655
+ }
1656
+ }
1657
+ outputJson({
1658
+ message: "Dry run - no changes applied",
1659
+ agent_id: agentId,
1660
+ agent_name: currentAgent.agent_name || "Unknown",
1661
+ fields_to_update: Object.keys(updateParams),
1662
+ changes: Object.keys(changes).length > 0 ? changes : "No changes detected (values are identical)",
1663
+ note: `Run without --dry-run to apply changes`
1664
+ });
1665
+ return;
1666
+ }
1667
+ const updatedAgent = await client.agent.update(
1668
+ agentId,
1669
+ updateParams
1670
+ );
1671
+ outputJson({
1672
+ message: "Agent updated successfully (draft version)",
1673
+ agent_id: updatedAgent.agent_id,
1674
+ agent_name: updatedAgent.agent_name || "Unknown",
1675
+ version: updatedAgent.version,
1676
+ fields_updated: Object.keys(updateParams),
1677
+ note: `Run 'retell agent-publish ${agentId}' to publish changes to production`
1678
+ });
1679
+ } catch (error) {
1680
+ if (error instanceof SyntaxError) {
1681
+ outputError(`Invalid JSON in file: ${error.message}`, "INVALID_JSON");
1682
+ return;
1683
+ }
1684
+ handleSdkError(error);
1685
+ }
1686
+ }
1687
+
1471
1688
  // src/services/tool-resolver.ts
1472
1689
  async function resolveToolsSource(agentId) {
1473
1690
  const client = getRetellClient();
1474
- const agent = await client.agent.retrieve(agentId);
1475
- if (agent.response_engine.type === "retell-llm") {
1476
- const llm = await client.llm.retrieve(agent.response_engine.llm_id);
1691
+ const agent2 = await client.agent.retrieve(agentId);
1692
+ if (agent2.response_engine.type === "retell-llm") {
1693
+ const llm = await client.llm.retrieve(agent2.response_engine.llm_id);
1477
1694
  const generalTools = llm.general_tools ?? [];
1478
1695
  const stateTools = {};
1479
1696
  if (llm.states) {
@@ -1492,16 +1709,16 @@ async function resolveToolsSource(agentId) {
1492
1709
  return {
1493
1710
  type: "retell-llm",
1494
1711
  agentId,
1495
- agentName: agent.agent_name ?? "Unknown",
1712
+ agentName: agent2.agent_name ?? "Unknown",
1496
1713
  llmId: llm.llm_id,
1497
1714
  generalTools,
1498
1715
  stateTools,
1499
1716
  totalCount
1500
1717
  };
1501
1718
  }
1502
- if (agent.response_engine.type === "conversation-flow") {
1719
+ if (agent2.response_engine.type === "conversation-flow") {
1503
1720
  const flow = await client.conversationFlow.retrieve(
1504
- agent.response_engine.conversation_flow_id
1721
+ agent2.response_engine.conversation_flow_id
1505
1722
  );
1506
1723
  const flowTools = flow.tools ?? [];
1507
1724
  const componentTools = {};
@@ -1521,7 +1738,7 @@ async function resolveToolsSource(agentId) {
1521
1738
  return {
1522
1739
  type: "conversation-flow",
1523
1740
  agentId,
1524
- agentName: agent.agent_name ?? "Unknown",
1741
+ agentName: agent2.agent_name ?? "Unknown",
1525
1742
  flowId: flow.conversation_flow_id,
1526
1743
  flowTools,
1527
1744
  componentTools,
@@ -1796,16 +2013,16 @@ async function getToolCommand(agentId, toolName, options) {
1796
2013
  }
1797
2014
 
1798
2015
  // src/commands/tools/add.ts
1799
- var import_fs5 = require("fs");
2016
+ var import_fs7 = require("fs");
1800
2017
  async function addToolCommand(agentId, options) {
1801
2018
  try {
1802
- if (!(0, import_fs5.existsSync)(options.file)) {
2019
+ if (!(0, import_fs7.existsSync)(options.file)) {
1803
2020
  outputError(`Tool file not found: ${options.file}`, "FILE_NOT_FOUND");
1804
2021
  return;
1805
2022
  }
1806
2023
  let tool;
1807
2024
  try {
1808
- const content = (0, import_fs5.readFileSync)(options.file, "utf-8");
2025
+ const content = (0, import_fs7.readFileSync)(options.file, "utf-8");
1809
2026
  tool = JSON.parse(content);
1810
2027
  } catch (error) {
1811
2028
  if (error instanceof SyntaxError) {
@@ -1980,16 +2197,16 @@ async function addToolCommand(agentId, options) {
1980
2197
  }
1981
2198
 
1982
2199
  // src/commands/tools/update.ts
1983
- var import_fs6 = require("fs");
2200
+ var import_fs8 = require("fs");
1984
2201
  async function updateToolCommand(agentId, toolName, options) {
1985
2202
  try {
1986
- if (!(0, import_fs6.existsSync)(options.file)) {
2203
+ if (!(0, import_fs8.existsSync)(options.file)) {
1987
2204
  outputError(`Tool file not found: ${options.file}`, "FILE_NOT_FOUND");
1988
2205
  return;
1989
2206
  }
1990
2207
  let newTool;
1991
2208
  try {
1992
- const content = (0, import_fs6.readFileSync)(options.file, "utf-8");
2209
+ const content = (0, import_fs8.readFileSync)(options.file, "utf-8");
1993
2210
  newTool = JSON.parse(content);
1994
2211
  } catch (error) {
1995
2212
  if (error instanceof SyntaxError) {
@@ -2373,7 +2590,7 @@ async function removeToolCommand(agentId, toolName, options) {
2373
2590
  }
2374
2591
 
2375
2592
  // src/commands/tools/export.ts
2376
- var import_fs7 = require("fs");
2593
+ var import_fs9 = require("fs");
2377
2594
  async function exportToolsCommand(agentId, options) {
2378
2595
  try {
2379
2596
  const source = await resolveToolsSource(agentId);
@@ -2421,7 +2638,7 @@ async function exportToolsCommand(agentId, options) {
2421
2638
  }
2422
2639
  if (options.output) {
2423
2640
  try {
2424
- (0, import_fs7.writeFileSync)(
2641
+ (0, import_fs9.writeFileSync)(
2425
2642
  options.output,
2426
2643
  JSON.stringify(exportData, null, 2),
2427
2644
  "utf-8"
@@ -2455,16 +2672,16 @@ async function exportToolsCommand(agentId, options) {
2455
2672
  }
2456
2673
 
2457
2674
  // src/commands/tools/import.ts
2458
- var import_fs8 = require("fs");
2675
+ var import_fs10 = require("fs");
2459
2676
  async function importToolsCommand(agentId, options) {
2460
2677
  try {
2461
- if (!(0, import_fs8.existsSync)(options.file)) {
2678
+ if (!(0, import_fs10.existsSync)(options.file)) {
2462
2679
  outputError(`Import file not found: ${options.file}`, "FILE_NOT_FOUND");
2463
2680
  return;
2464
2681
  }
2465
2682
  let importData;
2466
2683
  try {
2467
- const content = (0, import_fs8.readFileSync)(options.file, "utf-8");
2684
+ const content = (0, import_fs10.readFileSync)(options.file, "utf-8");
2468
2685
  importData = JSON.parse(content);
2469
2686
  } catch (error) {
2470
2687
  if (error instanceof SyntaxError) {
@@ -2864,7 +3081,7 @@ async function getTestCaseCommand(testCaseDefinitionId, options) {
2864
3081
  }
2865
3082
 
2866
3083
  // src/commands/tests/cases/create.ts
2867
- var import_fs9 = require("fs");
3084
+ var import_fs11 = require("fs");
2868
3085
  function buildResponseEngine2(options) {
2869
3086
  if (options.llmId && options.flowId) {
2870
3087
  outputError(
@@ -2902,7 +3119,7 @@ function buildResponseEngine2(options) {
2902
3119
  }
2903
3120
  async function createTestCaseCommand(options) {
2904
3121
  try {
2905
- if (!(0, import_fs9.existsSync)(options.file)) {
3122
+ if (!(0, import_fs11.existsSync)(options.file)) {
2906
3123
  outputError(
2907
3124
  `Test case file not found: ${options.file}`,
2908
3125
  "FILE_NOT_FOUND"
@@ -2911,7 +3128,7 @@ async function createTestCaseCommand(options) {
2911
3128
  }
2912
3129
  let input;
2913
3130
  try {
2914
- const content = (0, import_fs9.readFileSync)(options.file, "utf-8");
3131
+ const content = (0, import_fs11.readFileSync)(options.file, "utf-8");
2915
3132
  input = JSON.parse(content);
2916
3133
  } catch (error) {
2917
3134
  if (error instanceof SyntaxError) {
@@ -2961,10 +3178,10 @@ async function createTestCaseCommand(options) {
2961
3178
  }
2962
3179
 
2963
3180
  // src/commands/tests/cases/update.ts
2964
- var import_fs10 = require("fs");
3181
+ var import_fs12 = require("fs");
2965
3182
  async function updateTestCaseCommand(testCaseDefinitionId, options) {
2966
3183
  try {
2967
- if (!(0, import_fs10.existsSync)(options.file)) {
3184
+ if (!(0, import_fs12.existsSync)(options.file)) {
2968
3185
  outputError(
2969
3186
  `Test case file not found: ${options.file}`,
2970
3187
  "FILE_NOT_FOUND"
@@ -2973,7 +3190,7 @@ async function updateTestCaseCommand(testCaseDefinitionId, options) {
2973
3190
  }
2974
3191
  let input;
2975
3192
  try {
2976
- const content = (0, import_fs10.readFileSync)(options.file, "utf-8");
3193
+ const content = (0, import_fs12.readFileSync)(options.file, "utf-8");
2977
3194
  input = JSON.parse(content);
2978
3195
  } catch (error) {
2979
3196
  if (error instanceof SyntaxError) {
@@ -3230,7 +3447,7 @@ async function getKnowledgeBaseCommand(knowledgeBaseId, options) {
3230
3447
  }
3231
3448
 
3232
3449
  // src/commands/kb/create.ts
3233
- var import_fs11 = require("fs");
3450
+ var import_fs13 = require("fs");
3234
3451
  async function createKnowledgeBaseCommand(options) {
3235
3452
  try {
3236
3453
  if (options.name.length > 40) {
@@ -3253,12 +3470,12 @@ async function createKnowledgeBaseCommand(options) {
3253
3470
  }
3254
3471
  }
3255
3472
  if (options.texts) {
3256
- if (!(0, import_fs11.existsSync)(options.texts)) {
3473
+ if (!(0, import_fs13.existsSync)(options.texts)) {
3257
3474
  outputError(`Texts file not found: ${options.texts}`, "FILE_NOT_FOUND");
3258
3475
  return;
3259
3476
  }
3260
3477
  try {
3261
- const content = (0, import_fs11.readFileSync)(options.texts, "utf-8");
3478
+ const content = (0, import_fs13.readFileSync)(options.texts, "utf-8");
3262
3479
  const textsData = JSON.parse(content);
3263
3480
  if (!Array.isArray(textsData)) {
3264
3481
  outputError(
@@ -3334,7 +3551,7 @@ async function deleteKnowledgeBaseCommand(knowledgeBaseId) {
3334
3551
  }
3335
3552
 
3336
3553
  // src/commands/kb/sources/add.ts
3337
- var import_fs12 = require("fs");
3554
+ var import_fs14 = require("fs");
3338
3555
  async function addKnowledgeBaseSourcesCommand(knowledgeBaseId, options) {
3339
3556
  try {
3340
3557
  if (!options.urls && !options.texts) {
@@ -3352,12 +3569,12 @@ async function addKnowledgeBaseSourcesCommand(knowledgeBaseId, options) {
3352
3569
  }
3353
3570
  }
3354
3571
  if (options.texts) {
3355
- if (!(0, import_fs12.existsSync)(options.texts)) {
3572
+ if (!(0, import_fs14.existsSync)(options.texts)) {
3356
3573
  outputError(`Texts file not found: ${options.texts}`, "FILE_NOT_FOUND");
3357
3574
  return;
3358
3575
  }
3359
3576
  try {
3360
- const content = (0, import_fs12.readFileSync)(options.texts, "utf-8");
3577
+ const content = (0, import_fs14.readFileSync)(options.texts, "utf-8");
3361
3578
  const textsData = JSON.parse(content);
3362
3579
  if (!Array.isArray(textsData)) {
3363
3580
  outputError(
@@ -3483,16 +3700,16 @@ async function getFlowCommand(conversationFlowId, options) {
3483
3700
  }
3484
3701
 
3485
3702
  // src/commands/flows/create.ts
3486
- var import_fs13 = require("fs");
3703
+ var import_fs15 = require("fs");
3487
3704
  async function createFlowCommand(options) {
3488
3705
  try {
3489
- if (!(0, import_fs13.existsSync)(options.file)) {
3706
+ if (!(0, import_fs15.existsSync)(options.file)) {
3490
3707
  outputError(`Flow file not found: ${options.file}`, "FILE_NOT_FOUND");
3491
3708
  return;
3492
3709
  }
3493
3710
  let flowConfig;
3494
3711
  try {
3495
- const content = (0, import_fs13.readFileSync)(options.file, "utf-8");
3712
+ const content = (0, import_fs15.readFileSync)(options.file, "utf-8");
3496
3713
  flowConfig = JSON.parse(content);
3497
3714
  } catch (error) {
3498
3715
  if (error instanceof SyntaxError) {
@@ -3530,16 +3747,16 @@ async function createFlowCommand(options) {
3530
3747
  }
3531
3748
 
3532
3749
  // src/commands/flows/update.ts
3533
- var import_fs14 = require("fs");
3750
+ var import_fs16 = require("fs");
3534
3751
  async function updateFlowCommand(conversationFlowId, options) {
3535
3752
  try {
3536
- if (!(0, import_fs14.existsSync)(options.file)) {
3753
+ if (!(0, import_fs16.existsSync)(options.file)) {
3537
3754
  outputError(`Flow file not found: ${options.file}`, "FILE_NOT_FOUND");
3538
3755
  return;
3539
3756
  }
3540
3757
  let flowUpdates;
3541
3758
  try {
3542
- const content = (0, import_fs14.readFileSync)(options.file, "utf-8");
3759
+ const content = (0, import_fs16.readFileSync)(options.file, "utf-8");
3543
3760
  flowUpdates = JSON.parse(content);
3544
3761
  } catch (error) {
3545
3762
  if (error instanceof SyntaxError) {
@@ -3591,9 +3808,81 @@ async function deleteFlowCommand(conversationFlowId) {
3591
3808
  }
3592
3809
  }
3593
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
+
3594
3883
  // src/index.ts
3595
3884
  var packageJson = JSON.parse(
3596
- (0, import_fs15.readFileSync)((0, import_path6.join)(__dirname, "../package.json"), "utf-8")
3885
+ (0, import_fs17.readFileSync)((0, import_path6.join)(__dirname, "../package.json"), "utf-8")
3597
3886
  );
3598
3887
  var program = new import_commander.Command();
3599
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);
@@ -3763,6 +4052,42 @@ Examples:
3763
4052
  fields: options.fields
3764
4053
  });
3765
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
+ });
3766
4091
  var prompts = program.command("prompts").description("Manage agent prompts");
3767
4092
  prompts.command("pull <agent_id>").description("Download agent prompts to a local file").option(
3768
4093
  "-o, --output <path>",
@@ -3818,6 +4143,68 @@ Examples:
3818
4143
  ).action(async (agentId) => {
3819
4144
  await publishAgentCommand(agentId);
3820
4145
  });
4146
+ var agent = program.command("agent").description(
4147
+ "Manage agent-level configuration (voice, webhooks, post-call analysis, etc.)"
4148
+ );
4149
+ agent.command("get <agent_id>").description("Get agent configuration").option(
4150
+ "--version <number>",
4151
+ "Specific version to retrieve (defaults to latest)"
4152
+ ).option(
4153
+ "--fields <fields>",
4154
+ "Comma-separated list of fields to return (e.g., agent_name,post_call_analysis_data)"
4155
+ ).addHelpText(
4156
+ "after",
4157
+ `
4158
+ Examples:
4159
+ $ retell agent get agent_123abc
4160
+ $ retell agent get agent_123abc --version 2
4161
+ $ retell agent get agent_123abc --fields agent_name,post_call_analysis_data
4162
+ $ retell agent get agent_123abc > config.json
4163
+ `
4164
+ ).action(async (agentId, options) => {
4165
+ await getAgentCommand(agentId, {
4166
+ version: options.version ? parseInt(options.version, 10) : void 0,
4167
+ fields: options.fields
4168
+ });
4169
+ });
4170
+ agent.command("update <agent_id>").description("Update agent configuration from a JSON file").requiredOption(
4171
+ "-f, --file <path>",
4172
+ "Path to JSON file containing agent configuration updates"
4173
+ ).option("--dry-run", "Preview changes without applying them").option(
4174
+ "--version <number>",
4175
+ "Specific version to update (defaults to latest draft)"
4176
+ ).addHelpText(
4177
+ "after",
4178
+ `
4179
+ Examples:
4180
+ $ retell agent update agent_123abc --file config.json
4181
+ $ retell agent update agent_123abc --file config.json --dry-run
4182
+ $ retell agent update agent_123abc --file analysis.json --version 2
4183
+
4184
+ Example JSON for post-call analysis:
4185
+ {
4186
+ "post_call_analysis_model": "claude-4.5-sonnet",
4187
+ "post_call_analysis_data": [
4188
+ {
4189
+ "name": "call_outcome",
4190
+ "type": "enum",
4191
+ "description": "Result of the call",
4192
+ "choices": ["successful", "unsuccessful", "callback_needed"]
4193
+ }
4194
+ ],
4195
+ "analysis_successful_prompt": "Determine if the issue was resolved.",
4196
+ "analysis_summary_prompt": "Summarize the call in 2 sentences."
4197
+ }
4198
+
4199
+ Note: Run 'retell agent-publish <agent_id>' after updating to publish changes.
4200
+ `
4201
+ ).action(async (agentId, options) => {
4202
+ await updateAgentCommand(agentId, {
4203
+ file: options.file,
4204
+ dryRun: options.dryRun,
4205
+ version: options.version ? parseInt(options.version, 10) : void 0
4206
+ });
4207
+ });
3821
4208
  var tools = program.command("tools").description("Manage agent tools (custom functions, webhooks, etc.)");
3822
4209
  tools.command("list <agent_id>").description("List all tools configured for an agent").option("--state <name>", "Filter by state name (Retell LLM only)").option("--component <id>", "Filter by component ID (Conversation Flow only)").option("--fields <fields>", "Comma-separated list of fields to return").addHelpText(
3823
4210
  "after",
@@ -4301,6 +4688,38 @@ Examples:
4301
4688
  ).action(async (conversationFlowId) => {
4302
4689
  await deleteFlowCommand(conversationFlowId);
4303
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
+ });
4304
4723
  program.parse(process.argv);
4305
4724
  if (!process.argv.slice(2).length) {
4306
4725
  program.outputHelp();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "retell-cli",
3
- "version": "1.2.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": {