retell-sync-cli 3.7.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -98673,6 +98673,9 @@ function diff(obj, newObj, options = { cyclesFix: true }, _stack = []) {
98673
98673
  return diffs;
98674
98674
  }
98675
98675
 
98676
+ // src/commands/deploy.ts
98677
+ import path17 from "path";
98678
+
98676
98679
  // node_modules/remeda/dist/lazyDataLastImpl-BDhrIOwR.js
98677
98680
  function e(e2, t, n) {
98678
98681
  let r = (n2) => e2(n2, ...t);
@@ -135192,7 +135195,7 @@ async function getTestCaseDefinitions(responseEngine) {
135192
135195
  params.set("version", String(responseEngine.version));
135193
135196
  }
135194
135197
  }
135195
- const response = await fetch(`https://api.retellai.com/list-test-case-definitions?${params}`, {
135198
+ const response = await fetch(`https://api.retellai.com/list-test-case-definitions?${params.toString()}`, {
135196
135199
  headers: {
135197
135200
  Authorization: `Bearer ${process.env.RETELL_API_KEY}`,
135198
135201
  "Content-Type": "application/json"
@@ -135213,6 +135216,73 @@ async function getTestCaseDefinitions(responseEngine) {
135213
135216
  }
135214
135217
  return result.data;
135215
135218
  }
135219
+ function canonicalizeTestCases(testCases) {
135220
+ return testCases.map(({ test_case_definition_id, response_engine: _10, ...rest }) => ({
135221
+ ...rest,
135222
+ _id: test_case_definition_id
135223
+ }));
135224
+ }
135225
+ async function getLocalTestCases(agentDirPath) {
135226
+ const testsDir = path15.join(agentDirPath, "tests");
135227
+ const metaFile = Bun.file(path15.join(testsDir, ".tests.json"));
135228
+ if (!await metaFile.exists()) {
135229
+ return [];
135230
+ }
135231
+ const metaContent = await metaFile.text();
135232
+ const metadata = await readJson2(metaContent, zod_default.object({
135233
+ response_engine: zod_default.object({}).passthrough(),
135234
+ test_cases: zod_default.array(zod_default.object({
135235
+ id: zod_default.string(),
135236
+ name: zod_default.string()
135237
+ }))
135238
+ }));
135239
+ const testCases = [];
135240
+ for (const testCaseMeta of metadata.test_cases) {
135241
+ const testCaseName = toSnakeCase(testCaseMeta.name);
135242
+ let configContent = null;
135243
+ let reader = readYaml;
135244
+ for (const ext of ["yaml", "yml", "jsonc", "json"]) {
135245
+ const configPath = path15.join(testsDir, `${testCaseName}.${ext}`);
135246
+ const file2 = Bun.file(configPath);
135247
+ if (await file2.exists()) {
135248
+ configContent = await file2.text();
135249
+ reader = ext === "jsonc" ? readJsonc : ext === "json" ? readJson2 : readYaml;
135250
+ break;
135251
+ }
135252
+ }
135253
+ if (!configContent) {
135254
+ console.warn(`Warning: Could not find config file for test case ${testCaseMeta.name}`);
135255
+ continue;
135256
+ }
135257
+ const config2 = await reader(configContent, zod_default.looseObject({}));
135258
+ const resolveFileContent = async (filePath) => {
135259
+ const normalizedPath = filePath.replace(/^\.\//, "");
135260
+ const fullPath = path15.join(testsDir, normalizedPath);
135261
+ const content = await Bun.file(fullPath).text();
135262
+ return content;
135263
+ };
135264
+ await resolveFilePlaceholders(config2, resolveFileContent);
135265
+ testCases.push({
135266
+ ...config2,
135267
+ _id: testCaseMeta.id
135268
+ });
135269
+ }
135270
+ return testCases;
135271
+ }
135272
+ async function updateTestCaseDefinition(testCaseId, update) {
135273
+ const response = await fetch(`https://api.retellai.com/update-test-case-definition/${testCaseId}`, {
135274
+ method: "PUT",
135275
+ headers: {
135276
+ Authorization: `Bearer ${process.env.RETELL_API_KEY}`,
135277
+ "Content-Type": "application/json"
135278
+ },
135279
+ body: JSON.stringify(update)
135280
+ });
135281
+ if (!response.ok) {
135282
+ const errorText = await response.text();
135283
+ throw new Error(`Failed to update test case ${testCaseId}: ${response.status} ${errorText}`);
135284
+ }
135285
+ }
135216
135286
  async function fetchAndWriteTestCases({
135217
135287
  state,
135218
135288
  agentsDir = DEFAULT_AGENTS_DIR,
@@ -135250,7 +135320,8 @@ async function fetchAndWriteTestCases({
135250
135320
  try {
135251
135321
  testCases = await getTestCaseDefinitions(normalizedEngine);
135252
135322
  } catch (err) {
135253
- console.warn(`Warning: Could not fetch test cases for ${agentName}: ${err}`);
135323
+ const message = err instanceof Error ? err.message : String(err);
135324
+ console.warn(`Warning: Could not fetch test cases for ${agentName}: ${message}`);
135254
135325
  continue;
135255
135326
  }
135256
135327
  if (testCases.length === 0) {
@@ -135805,10 +135876,78 @@ async function deploy({
135805
135876
  const totalRemoteAgents = remoteState.voiceAgents.length + remoteState.chatAgents.length;
135806
135877
  spinner.stop(source_default.dim(`Local: ${totalLocalAgents} agents | Remote: ${totalRemoteAgents} agents`));
135807
135878
  spinner = createSpinner2("Computing differences...");
135808
- const changes = computeChanges(localState, remoteState);
135879
+ const baseChanges = computeChanges(localState, remoteState);
135880
+ const testCaseChanges = [];
135881
+ const allAgents = [
135882
+ ...localState.voiceAgents.map((a7) => ({
135883
+ ...a7,
135884
+ agentType: "voice"
135885
+ })),
135886
+ ...localState.chatAgents.map((a7) => ({ ...a7, agentType: "chat" }))
135887
+ ];
135888
+ for (const agent of allAgents) {
135889
+ if (agent.response_engine.type !== "retell-llm" && agent.response_engine.type !== "conversation-flow") {
135890
+ continue;
135891
+ }
135892
+ const agentName = agent.agent_name ?? agent._id;
135893
+ const hash2 = agent._id.slice(-FILE_HASH_LENGTH);
135894
+ const agentDirName = `${toSnakeCase(agentName)}_${hash2}`;
135895
+ const agentDirPath = path17.join(agentsDir, agentDirName);
135896
+ const localTestCases = await getLocalTestCases(agentDirPath);
135897
+ if (localTestCases.length === 0)
135898
+ continue;
135899
+ const normalizedEngine = agent.response_engine.type === "retell-llm" ? {
135900
+ type: "retell-llm",
135901
+ llm_id: agent.response_engine.llm_id,
135902
+ version: agent.response_engine.version ?? undefined
135903
+ } : {
135904
+ type: "conversation-flow",
135905
+ conversation_flow_id: agent.response_engine.conversation_flow_id,
135906
+ version: agent.response_engine.version ?? undefined
135907
+ };
135908
+ let remoteTestCases = [];
135909
+ try {
135910
+ const rawRemote = await getTestCaseDefinitions(normalizedEngine);
135911
+ remoteTestCases = canonicalizeTestCases(rawRemote);
135912
+ } catch {
135913
+ continue;
135914
+ }
135915
+ const testCaseMetadataFields = [
135916
+ "_id",
135917
+ "creation_timestamp",
135918
+ "user_modified_timestamp",
135919
+ "type"
135920
+ ];
135921
+ const remoteTestCaseMap = new Map(remoteTestCases.map((tc3) => [tc3._id, tc3]));
135922
+ for (const localTC of localTestCases) {
135923
+ const remoteTC = remoteTestCaseMap.get(localTC._id);
135924
+ if (!remoteTC)
135925
+ continue;
135926
+ const normalizePrompt = (s5) => s5.trim().replace(/\r\n/g, `
135927
+ `);
135928
+ const localComparable = {
135929
+ ...n9(localTC, testCaseMetadataFields),
135930
+ user_prompt: normalizePrompt(localTC.user_prompt)
135931
+ };
135932
+ const remoteComparable = {
135933
+ ...n9(remoteTC, testCaseMetadataFields),
135934
+ user_prompt: normalizePrompt(remoteTC.user_prompt)
135935
+ };
135936
+ const differences = diff(remoteComparable, localComparable);
135937
+ if (differences.length > 0) {
135938
+ testCaseChanges.push({
135939
+ id: localTC._id,
135940
+ name: localTC.name,
135941
+ current: localTC,
135942
+ differences
135943
+ });
135944
+ }
135945
+ }
135946
+ }
135947
+ const changes = { ...baseChanges, testCases: testCaseChanges };
135809
135948
  const totalAgentChanges = changes.voiceAgents.length + changes.chatAgents.length;
135810
- spinner.stop(source_default.dim(`Found ${source_default.white(totalAgentChanges)} agent changes, ${source_default.white(changes.llms.length)} LLM changes, ${source_default.white(changes.flows.length)} flow changes`));
135811
- const totalChanges = totalAgentChanges + changes.llms.length + changes.flows.length;
135949
+ spinner.stop(source_default.dim(`Found ${source_default.white(totalAgentChanges)} agent changes, ${source_default.white(changes.llms.length)} LLM changes, ${source_default.white(changes.flows.length)} flow changes, ${source_default.white(changes.testCases.length)} test case changes`));
135950
+ const totalChanges = totalAgentChanges + changes.llms.length + changes.flows.length + changes.testCases.length;
135812
135951
  const affectedAgentIds = new Set;
135813
135952
  for (const change of changes.voiceAgents) {
135814
135953
  affectedAgentIds.add(change.id);
@@ -135868,6 +136007,11 @@ async function deploy({
135868
136007
  const { _id, _version, ...updateData } = change.current;
135869
136008
  await retell.conversationFlow.update(_id, updateData);
135870
136009
  return { type: "flow", id: _id, name: change.name };
136010
+ }),
136011
+ ...changes.testCases.map(async (change) => {
136012
+ const { _id, ...updateData } = change.current;
136013
+ await updateTestCaseDefinition(_id, updateData);
136014
+ return { type: "test case", id: _id, name: change.name };
135871
136015
  })
135872
136016
  ]);
135873
136017
  spinner.stop(source_default.dim("Done"));
@@ -136059,6 +136203,16 @@ Flows to update:`));
136059
136203
  }
136060
136204
  }
136061
136205
  }
136206
+ if (changes.testCases.length > 0) {
136207
+ console.log(source_default.cyan(`
136208
+ Test cases to update:`));
136209
+ for (const change of changes.testCases) {
136210
+ console.log(` ${source_default.bold(change.name)} ${source_default.dim(`(${change.id})`)}`);
136211
+ for (const d5 of change.differences) {
136212
+ printDiff(d5, { verbose });
136213
+ }
136214
+ }
136215
+ }
136062
136216
  }
136063
136217
 
136064
136218
  // src/commands/publish.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "retell-sync-cli",
3
- "version": "3.7.0",
3
+ "version": "3.8.0",
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
- }