retell-sync-cli 3.6.0 → 3.7.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 +203 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -135137,6 +135137,193 @@ 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}`, {
|
|
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
|
+
console.warn(`Warning: Could not fetch test cases for ${agentName}: ${err}`);
|
|
135254
|
+
continue;
|
|
135255
|
+
}
|
|
135256
|
+
if (testCases.length === 0) {
|
|
135257
|
+
const testsDir = path15.join(agentDirPath, "tests");
|
|
135258
|
+
await fs8.rm(testsDir, { recursive: true, force: true }).catch(() => {});
|
|
135259
|
+
continue;
|
|
135260
|
+
}
|
|
135261
|
+
await writeTestCases(testCases, {
|
|
135262
|
+
agentDirPath,
|
|
135263
|
+
responseEngine: normalizedEngine,
|
|
135264
|
+
configFormat
|
|
135265
|
+
});
|
|
135266
|
+
results.push({ agentDir: agentDirName, testCount: testCases.length });
|
|
135267
|
+
}
|
|
135268
|
+
return results;
|
|
135269
|
+
}
|
|
135270
|
+
async function writeTestCases(testCases, {
|
|
135271
|
+
agentDirPath,
|
|
135272
|
+
responseEngine,
|
|
135273
|
+
configFormat = DEFAULT_CONFIG_FORMAT
|
|
135274
|
+
}) {
|
|
135275
|
+
const testsDir = path15.join(agentDirPath, "tests");
|
|
135276
|
+
await fs8.mkdir(testsDir, { recursive: true });
|
|
135277
|
+
const isYaml = configFormat === "yaml" || configFormat === "yml";
|
|
135278
|
+
const isJsonc = configFormat === "jsonc";
|
|
135279
|
+
const configExt = configFormat;
|
|
135280
|
+
const metadata = {
|
|
135281
|
+
response_engine: responseEngine,
|
|
135282
|
+
test_cases: testCases.map((tc3) => ({
|
|
135283
|
+
id: tc3.test_case_definition_id,
|
|
135284
|
+
name: tc3.name
|
|
135285
|
+
}))
|
|
135286
|
+
};
|
|
135287
|
+
await Bun.write(path15.join(testsDir, ".tests.json"), await writeJson(metadata));
|
|
135288
|
+
const writtenFiles = new Set([".tests.json"]);
|
|
135289
|
+
for (const testCase of testCases) {
|
|
135290
|
+
const {
|
|
135291
|
+
test_case_definition_id: _id,
|
|
135292
|
+
response_engine: _engine,
|
|
135293
|
+
creation_timestamp: _created,
|
|
135294
|
+
user_modified_timestamp: _modified,
|
|
135295
|
+
type: _type,
|
|
135296
|
+
...config2
|
|
135297
|
+
} = testCase;
|
|
135298
|
+
const testCaseName = toSnakeCase(testCase.name);
|
|
135299
|
+
const promptFileName = `${testCaseName}_prompt.md`;
|
|
135300
|
+
const promptFilePath = path15.join(testsDir, promptFileName);
|
|
135301
|
+
await Bun.write(promptFilePath, await writeMarkdown(config2.user_prompt));
|
|
135302
|
+
writtenFiles.add(promptFileName);
|
|
135303
|
+
const configWithFileRef = {
|
|
135304
|
+
...config2,
|
|
135305
|
+
user_prompt: `file://./${promptFileName}`
|
|
135306
|
+
};
|
|
135307
|
+
const configFileName = `${testCaseName}.${configExt}`;
|
|
135308
|
+
const configContent = isYaml ? await writeYaml(configWithFileRef, { comments: testCaseFieldDocs }) : isJsonc ? await writeJsonc(configWithFileRef, { comments: testCaseFieldDocs }) : await writeJson(configWithFileRef);
|
|
135309
|
+
await Bun.write(path15.join(testsDir, configFileName), configContent);
|
|
135310
|
+
writtenFiles.add(configFileName);
|
|
135311
|
+
}
|
|
135312
|
+
const existingFiles = await fs8.readdir(testsDir).catch(() => []);
|
|
135313
|
+
for (const file2 of existingFiles) {
|
|
135314
|
+
if (!writtenFiles.has(file2)) {
|
|
135315
|
+
await fs8.rm(path15.join(testsDir, file2)).catch(() => {});
|
|
135316
|
+
}
|
|
135317
|
+
}
|
|
135318
|
+
}
|
|
135319
|
+
var testCaseFieldDocs = {
|
|
135320
|
+
name: "Name of the test case",
|
|
135321
|
+
user_prompt: "Prompt describing simulated user behavior (file reference)",
|
|
135322
|
+
metrics: "Array of evaluation criteria to check",
|
|
135323
|
+
dynamic_variables: "Variables injected into the agent during test",
|
|
135324
|
+
tool_mocks: "Mock responses for tool/function calls",
|
|
135325
|
+
llm_model: "LLM model used to simulate the user"
|
|
135326
|
+
};
|
|
135140
135327
|
|
|
135141
135328
|
// src/lib/logger.ts
|
|
135142
135329
|
var quietMode = false;
|
|
@@ -135515,7 +135702,8 @@ async function pullCommand(agentIdArgs, opts, cmd) {
|
|
|
135515
135702
|
agentsDir: globalOpts.agentsDir,
|
|
135516
135703
|
configFormat: globalOpts.configFormat,
|
|
135517
135704
|
agentIds,
|
|
135518
|
-
yes: opts.yes
|
|
135705
|
+
yes: opts.yes,
|
|
135706
|
+
tests: opts.tests ?? true
|
|
135519
135707
|
});
|
|
135520
135708
|
} catch (err) {
|
|
135521
135709
|
if (err instanceof ExitPromptError) {
|
|
@@ -135529,7 +135717,8 @@ async function pull({
|
|
|
135529
135717
|
agentsDir = DEFAULT_AGENTS_DIR,
|
|
135530
135718
|
configFormat = DEFAULT_CONFIG_FORMAT,
|
|
135531
135719
|
agentIds = null,
|
|
135532
|
-
yes = false
|
|
135720
|
+
yes = false,
|
|
135721
|
+
tests = true
|
|
135533
135722
|
} = {}) {
|
|
135534
135723
|
const scopeLabel = agentIds ? `${agentIds.length} agent(s)` : "all agents";
|
|
135535
135724
|
console.log(source_default.bold(`Pulling ${scopeLabel} from Retell...`));
|
|
@@ -135555,6 +135744,17 @@ async function pull({
|
|
|
135555
135744
|
const writeSpinner = createSpinner("Writing files...");
|
|
135556
135745
|
await writeState(remoteState, { agentsDir, configFormat, agentIds });
|
|
135557
135746
|
writeSpinner.stop(source_default.green("Done"));
|
|
135747
|
+
if (tests) {
|
|
135748
|
+
const testSpinner = createSpinner("Fetching test cases...");
|
|
135749
|
+
const testResults = await fetchAndWriteTestCases({
|
|
135750
|
+
state: remoteState,
|
|
135751
|
+
agentsDir,
|
|
135752
|
+
configFormat
|
|
135753
|
+
});
|
|
135754
|
+
const totalTests = testResults.reduce((sum, r10) => sum + r10.testCount, 0);
|
|
135755
|
+
const agentsWithTests = testResults.filter((r10) => r10.testCount > 0).length;
|
|
135756
|
+
testSpinner.stop(source_default.dim(`${pluralize("test case", totalTests, true)} across ${pluralize("agent", agentsWithTests, true)}`));
|
|
135757
|
+
}
|
|
135558
135758
|
console.log(source_default.dim(`Files written to ${source_default.bold(agentsDir)}. Review with git diff.`));
|
|
135559
135759
|
}
|
|
135560
135760
|
|
|
@@ -136173,7 +136373,7 @@ async function updatePhoneNumberVersions(publishedAgentIds, agentNames) {
|
|
|
136173
136373
|
// src/cli.ts
|
|
136174
136374
|
var program2 = new Command;
|
|
136175
136375
|
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);
|
|
136376
|
+
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
136377
|
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
136378
|
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
136379
|
program2.parse();
|