retell-sync-cli 3.6.0 → 3.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -135137,6 +135137,194 @@ async function canonicalizeFromFiles(files) {
135137
135137
  conversationFlows
135138
135138
  };
135139
135139
  }
135140
+ var InputMatchRuleSchema = zod_default.discriminatedUnion("type", [
135141
+ zod_default.object({ type: zod_default.literal("any") }),
135142
+ zod_default.object({
135143
+ type: zod_default.literal("exact"),
135144
+ input: zod_default.record(zod_default.string(), zod_default.unknown())
135145
+ }),
135146
+ zod_default.object({
135147
+ type: zod_default.literal("partial"),
135148
+ input: zod_default.record(zod_default.string(), zod_default.unknown())
135149
+ })
135150
+ ]);
135151
+ var ToolMockSchema = zod_default.object({
135152
+ tool_name: zod_default.string(),
135153
+ input_match_rule: InputMatchRuleSchema,
135154
+ output: zod_default.string(),
135155
+ result: zod_default.boolean()
135156
+ });
135157
+ var TestCaseResponseEngineSchema = zod_default.discriminatedUnion("type", [
135158
+ zod_default.object({
135159
+ type: zod_default.literal("retell-llm"),
135160
+ llm_id: zod_default.string(),
135161
+ version: zod_default.number().optional()
135162
+ }),
135163
+ zod_default.object({
135164
+ type: zod_default.literal("conversation-flow"),
135165
+ conversation_flow_id: zod_default.string(),
135166
+ version: zod_default.number().optional()
135167
+ })
135168
+ ]);
135169
+ var TestCaseDefinitionSchema = zod_default.object({
135170
+ test_case_definition_id: zod_default.string(),
135171
+ name: zod_default.string(),
135172
+ response_engine: TestCaseResponseEngineSchema,
135173
+ dynamic_variables: zod_default.record(zod_default.string(), zod_default.unknown()).optional().default({}),
135174
+ metrics: zod_default.array(zod_default.string()).optional().default([]),
135175
+ user_prompt: zod_default.string(),
135176
+ creation_timestamp: zod_default.number(),
135177
+ user_modified_timestamp: zod_default.number(),
135178
+ type: zod_default.literal("simulation"),
135179
+ tool_mocks: zod_default.array(ToolMockSchema).optional().default([]),
135180
+ llm_model: zod_default.string()
135181
+ });
135182
+ async function getTestCaseDefinitions(responseEngine) {
135183
+ const params = new URLSearchParams({ type: responseEngine.type });
135184
+ if (responseEngine.type === "retell-llm") {
135185
+ params.set("llm_id", responseEngine.llm_id);
135186
+ if (responseEngine.version != null) {
135187
+ params.set("version", String(responseEngine.version));
135188
+ }
135189
+ } else {
135190
+ params.set("conversation_flow_id", responseEngine.conversation_flow_id);
135191
+ if (responseEngine.version != null) {
135192
+ params.set("version", String(responseEngine.version));
135193
+ }
135194
+ }
135195
+ const response = await fetch(`https://api.retellai.com/list-test-case-definitions?${params.toString()}`, {
135196
+ headers: {
135197
+ Authorization: `Bearer ${process.env.RETELL_API_KEY}`,
135198
+ "Content-Type": "application/json"
135199
+ }
135200
+ });
135201
+ if (!response.ok) {
135202
+ const errorText = await response.text();
135203
+ if (response.status === 404) {
135204
+ return [];
135205
+ }
135206
+ throw new Error(`Failed to fetch test cases: ${response.status} ${errorText}`);
135207
+ }
135208
+ const data = await response.json();
135209
+ const result = zod_default.array(TestCaseDefinitionSchema).safeParse(data);
135210
+ if (!result.success) {
135211
+ console.warn("Warning: Some test case fields failed validation:", result.error.issues);
135212
+ return [];
135213
+ }
135214
+ return result.data;
135215
+ }
135216
+ async function fetchAndWriteTestCases({
135217
+ state,
135218
+ agentsDir = DEFAULT_AGENTS_DIR,
135219
+ configFormat = DEFAULT_CONFIG_FORMAT
135220
+ }) {
135221
+ const results = [];
135222
+ const allAgents = [
135223
+ ...state.voiceAgents.map((a7) => ({
135224
+ ...a7,
135225
+ agentType: "voice"
135226
+ })),
135227
+ ...state.chatAgents.map((a7) => ({
135228
+ ...a7,
135229
+ agentType: "chat"
135230
+ }))
135231
+ ];
135232
+ for (const agent of allAgents) {
135233
+ const agentName = agent.agent_name ?? agent._id;
135234
+ const hash2 = agent._id.slice(-FILE_HASH_LENGTH);
135235
+ const agentDirName = `${toSnakeCase(agentName)}_${hash2}`;
135236
+ const agentDirPath = path15.join(agentsDir, agentDirName);
135237
+ if (agent.response_engine.type !== "retell-llm" && agent.response_engine.type !== "conversation-flow") {
135238
+ continue;
135239
+ }
135240
+ const normalizedEngine = agent.response_engine.type === "retell-llm" ? {
135241
+ type: "retell-llm",
135242
+ llm_id: agent.response_engine.llm_id,
135243
+ version: agent.response_engine.version ?? undefined
135244
+ } : {
135245
+ type: "conversation-flow",
135246
+ conversation_flow_id: agent.response_engine.conversation_flow_id,
135247
+ version: agent.response_engine.version ?? undefined
135248
+ };
135249
+ let testCases;
135250
+ try {
135251
+ testCases = await getTestCaseDefinitions(normalizedEngine);
135252
+ } catch (err) {
135253
+ const message = err instanceof Error ? err.message : String(err);
135254
+ console.warn(`Warning: Could not fetch test cases for ${agentName}: ${message}`);
135255
+ continue;
135256
+ }
135257
+ if (testCases.length === 0) {
135258
+ const testsDir = path15.join(agentDirPath, "tests");
135259
+ await fs8.rm(testsDir, { recursive: true, force: true }).catch(() => {});
135260
+ continue;
135261
+ }
135262
+ await writeTestCases(testCases, {
135263
+ agentDirPath,
135264
+ responseEngine: normalizedEngine,
135265
+ configFormat
135266
+ });
135267
+ results.push({ agentDir: agentDirName, testCount: testCases.length });
135268
+ }
135269
+ return results;
135270
+ }
135271
+ async function writeTestCases(testCases, {
135272
+ agentDirPath,
135273
+ responseEngine,
135274
+ configFormat = DEFAULT_CONFIG_FORMAT
135275
+ }) {
135276
+ const testsDir = path15.join(agentDirPath, "tests");
135277
+ await fs8.mkdir(testsDir, { recursive: true });
135278
+ const isYaml = configFormat === "yaml" || configFormat === "yml";
135279
+ const isJsonc = configFormat === "jsonc";
135280
+ const configExt = configFormat;
135281
+ const metadata = {
135282
+ response_engine: responseEngine,
135283
+ test_cases: testCases.map((tc3) => ({
135284
+ id: tc3.test_case_definition_id,
135285
+ name: tc3.name
135286
+ }))
135287
+ };
135288
+ await Bun.write(path15.join(testsDir, ".tests.json"), await writeJson(metadata));
135289
+ const writtenFiles = new Set([".tests.json"]);
135290
+ for (const testCase of testCases) {
135291
+ const {
135292
+ test_case_definition_id: _id,
135293
+ response_engine: _engine,
135294
+ creation_timestamp: _created,
135295
+ user_modified_timestamp: _modified,
135296
+ type: _type,
135297
+ ...config2
135298
+ } = testCase;
135299
+ const testCaseName = toSnakeCase(testCase.name);
135300
+ const promptFileName = `${testCaseName}_prompt.md`;
135301
+ const promptFilePath = path15.join(testsDir, promptFileName);
135302
+ await Bun.write(promptFilePath, await writeMarkdown(config2.user_prompt));
135303
+ writtenFiles.add(promptFileName);
135304
+ const configWithFileRef = {
135305
+ ...config2,
135306
+ user_prompt: `file://./${promptFileName}`
135307
+ };
135308
+ const configFileName = `${testCaseName}.${configExt}`;
135309
+ const configContent = isYaml ? await writeYaml(configWithFileRef, { comments: testCaseFieldDocs }) : isJsonc ? await writeJsonc(configWithFileRef, { comments: testCaseFieldDocs }) : await writeJson(configWithFileRef);
135310
+ await Bun.write(path15.join(testsDir, configFileName), configContent);
135311
+ writtenFiles.add(configFileName);
135312
+ }
135313
+ const existingFiles = await fs8.readdir(testsDir).catch(() => []);
135314
+ for (const file2 of existingFiles) {
135315
+ if (!writtenFiles.has(file2)) {
135316
+ await fs8.rm(path15.join(testsDir, file2)).catch(() => {});
135317
+ }
135318
+ }
135319
+ }
135320
+ var testCaseFieldDocs = {
135321
+ name: "Name of the test case",
135322
+ user_prompt: "Prompt describing simulated user behavior (file reference)",
135323
+ metrics: "Array of evaluation criteria to check",
135324
+ dynamic_variables: "Variables injected into the agent during test",
135325
+ tool_mocks: "Mock responses for tool/function calls",
135326
+ llm_model: "LLM model used to simulate the user"
135327
+ };
135140
135328
 
135141
135329
  // src/lib/logger.ts
135142
135330
  var quietMode = false;
@@ -135515,7 +135703,8 @@ async function pullCommand(agentIdArgs, opts, cmd) {
135515
135703
  agentsDir: globalOpts.agentsDir,
135516
135704
  configFormat: globalOpts.configFormat,
135517
135705
  agentIds,
135518
- yes: opts.yes
135706
+ yes: opts.yes,
135707
+ tests: opts.tests ?? true
135519
135708
  });
135520
135709
  } catch (err) {
135521
135710
  if (err instanceof ExitPromptError) {
@@ -135529,7 +135718,8 @@ async function pull({
135529
135718
  agentsDir = DEFAULT_AGENTS_DIR,
135530
135719
  configFormat = DEFAULT_CONFIG_FORMAT,
135531
135720
  agentIds = null,
135532
- yes = false
135721
+ yes = false,
135722
+ tests = true
135533
135723
  } = {}) {
135534
135724
  const scopeLabel = agentIds ? `${agentIds.length} agent(s)` : "all agents";
135535
135725
  console.log(source_default.bold(`Pulling ${scopeLabel} from Retell...`));
@@ -135555,6 +135745,17 @@ async function pull({
135555
135745
  const writeSpinner = createSpinner("Writing files...");
135556
135746
  await writeState(remoteState, { agentsDir, configFormat, agentIds });
135557
135747
  writeSpinner.stop(source_default.green("Done"));
135748
+ if (tests) {
135749
+ const testSpinner = createSpinner("Fetching test cases...");
135750
+ const testResults = await fetchAndWriteTestCases({
135751
+ state: remoteState,
135752
+ agentsDir,
135753
+ configFormat
135754
+ });
135755
+ const totalTests = testResults.reduce((sum, r10) => sum + r10.testCount, 0);
135756
+ const agentsWithTests = testResults.filter((r10) => r10.testCount > 0).length;
135757
+ testSpinner.stop(source_default.dim(`${pluralize("test case", totalTests, true)} across ${pluralize("agent", agentsWithTests, true)}`));
135758
+ }
135558
135759
  console.log(source_default.dim(`Files written to ${source_default.bold(agentsDir)}. Review with git diff.`));
135559
135760
  }
135560
135761
 
@@ -136173,7 +136374,7 @@ async function updatePhoneNumberVersions(publishedAgentIds, agentNames) {
136173
136374
  // src/cli.ts
136174
136375
  var program2 = new Command;
136175
136376
  program2.name("retell").description("Retell AI agent management CLI").option("-w, --agents-dir <dir>", "Directory for agent files", DEFAULT_AGENTS_DIR).option("-f, --config-format <format>", `Config file format (${CONFIG_FORMATS.join(", ")})`, DEFAULT_CONFIG_FORMAT);
136176
- program2.command("pull [agentIds...]").description("Pull agents from Retell API (always pulls latest draft state)").option("-a, --all", "Pull all agents in the account").option("-s, --select", "Force interactive agent selection").option("-y, --yes", "Skip confirmation prompts").action(pullCommand);
136377
+ program2.command("pull [agentIds...]").description("Pull agents from Retell API (always pulls latest draft state)").option("-a, --all", "Pull all agents in the account").option("-s, --select", "Force interactive agent selection").option("-y, --yes", "Skip confirmation prompts").option("--no-tests", "Skip pulling test case definitions").action(pullCommand);
136177
136378
  program2.command("deploy [agentIds...]").description("Deploy local changes to Retell draft").option("-a, --all", "Deploy all agents in the account").option("-s, --select", "Force interactive agent selection").option("-n, --dry-run", "Show changes without applying").option("-v, --verbose", "Show full diff details (use with --dry-run)").option("-q, --quiet", "Output only affected agent IDs (for piping)").action(deployCommand);
136178
136379
  program2.command("publish [agentIds...]").description("Publish agents with unpublished draft changes").option("-a, --all", "Publish all agents in the account").option("-s, --select", "Force interactive agent selection").option("-n, --dry-run", "Show what would be published without publishing").option("-q, --quiet", "Output only published agent IDs (for piping)").action(publishCommand);
136179
136380
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "retell-sync-cli",
3
- "version": "3.6.0",
3
+ "version": "3.7.2",
4
4
  "description": "CLI tool for syncing Retell AI agents between local filesystem and API",
5
5
  "keywords": [
6
6
  "agents",
package/.env.local DELETED
@@ -1 +0,0 @@
1
- RETELL_API_KEY="key_12a24954adfaf146f4a3cd6616a3"
package/.oxlintrc.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "plugins": [
3
- "eslint",
4
- "typescript",
5
- "unicorn",
6
- "oxc",
7
- "import",
8
- "jsdoc",
9
- "node",
10
- "promise"
11
- ]
12
- }
package/.prettierrc.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "$schema": "https://www.schemastore.org/prettierrc.json",
3
- "semi": false,
4
- "tabWidth": 2,
5
- "useTabs": false,
6
- "plugins": ["prettier-plugin-jsdoc"]
7
- }
@@ -1,9 +0,0 @@
1
- {
2
- "editor.defaultFormatter": "esbenp.prettier-vscode",
3
- "editor.formatOnSave": true,
4
- "oxc.typeAware": true,
5
- "biome.enabled": false,
6
- "[typescript]": {
7
- "editor.defaultFormatter": "esbenp.prettier-vscode"
8
- }
9
- }