@chanl-ai/cli 2.0.1
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/bin/chanl.js +10 -0
- package/dist/__tests__/cli.test.d.ts +2 -0
- package/dist/__tests__/cli.test.js +2313 -0
- package/dist/__tests__/cli.test.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +72 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/agents.d.ts +8 -0
- package/dist/commands/agents.js +671 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/auth.d.ts +16 -0
- package/dist/commands/auth.js +294 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/call.d.ts +8 -0
- package/dist/commands/call.js +166 -0
- package/dist/commands/call.js.map +1 -0
- package/dist/commands/calls.d.ts +8 -0
- package/dist/commands/calls.js +719 -0
- package/dist/commands/calls.js.map +1 -0
- package/dist/commands/chat.d.ts +8 -0
- package/dist/commands/chat.js +203 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.js +231 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/health.d.ts +8 -0
- package/dist/commands/health.js +55 -0
- package/dist/commands/health.js.map +1 -0
- package/dist/commands/index.d.ts +18 -0
- package/dist/commands/index.js +39 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/knowledge.d.ts +8 -0
- package/dist/commands/knowledge.js +539 -0
- package/dist/commands/knowledge.js.map +1 -0
- package/dist/commands/mcp.d.ts +8 -0
- package/dist/commands/mcp.js +589 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/memory.d.ts +8 -0
- package/dist/commands/memory.js +408 -0
- package/dist/commands/memory.js.map +1 -0
- package/dist/commands/personas.d.ts +8 -0
- package/dist/commands/personas.js +356 -0
- package/dist/commands/personas.js.map +1 -0
- package/dist/commands/prompts.d.ts +8 -0
- package/dist/commands/prompts.js +295 -0
- package/dist/commands/prompts.js.map +1 -0
- package/dist/commands/scenarios.d.ts +8 -0
- package/dist/commands/scenarios.js +591 -0
- package/dist/commands/scenarios.js.map +1 -0
- package/dist/commands/scorecards.d.ts +8 -0
- package/dist/commands/scorecards.js +570 -0
- package/dist/commands/scorecards.js.map +1 -0
- package/dist/commands/tools.d.ts +8 -0
- package/dist/commands/tools.js +632 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/commands/toolsets.d.ts +8 -0
- package/dist/commands/toolsets.js +464 -0
- package/dist/commands/toolsets.js.map +1 -0
- package/dist/commands/workspaces.d.ts +8 -0
- package/dist/commands/workspaces.js +170 -0
- package/dist/commands/workspaces.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/config-store.d.ts +117 -0
- package/dist/utils/config-store.js +191 -0
- package/dist/utils/config-store.js.map +1 -0
- package/dist/utils/interactive.d.ts +41 -0
- package/dist/utils/interactive.js +83 -0
- package/dist/utils/interactive.js.map +1 -0
- package/dist/utils/output.d.ts +100 -0
- package/dist/utils/output.js +221 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/sdk-factory.d.ts +15 -0
- package/dist/utils/sdk-factory.js +34 -0
- package/dist/utils/sdk-factory.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { createSdk } from "../utils/sdk-factory.js";
|
|
5
|
+
import {
|
|
6
|
+
printError,
|
|
7
|
+
printInfo,
|
|
8
|
+
printBlank,
|
|
9
|
+
printSimpleTable,
|
|
10
|
+
printLabel,
|
|
11
|
+
isJsonOutput,
|
|
12
|
+
printJson,
|
|
13
|
+
formatDate
|
|
14
|
+
} from "../utils/output.js";
|
|
15
|
+
import {
|
|
16
|
+
formatElapsed,
|
|
17
|
+
printSegment,
|
|
18
|
+
displayScorecard
|
|
19
|
+
} from "../utils/interactive.js";
|
|
20
|
+
function createScenariosCommand() {
|
|
21
|
+
const scenarios = new Command("scenarios").description("Manage and run test scenarios").addHelpText(
|
|
22
|
+
"after",
|
|
23
|
+
`
|
|
24
|
+
What are Scenarios?
|
|
25
|
+
Scenarios are test cases that run against your AI agents. Each scenario
|
|
26
|
+
defines a conversation flow, expected responses, and scoring criteria.
|
|
27
|
+
|
|
28
|
+
Quick Start:
|
|
29
|
+
$ chanl scenarios list # See all scenarios
|
|
30
|
+
$ chanl scenarios run <id> # Execute a scenario
|
|
31
|
+
$ chanl scenarios run <id> --watch # Execute and watch progress
|
|
32
|
+
$ chanl scenarios history <id> # View past runs
|
|
33
|
+
|
|
34
|
+
Execution Workflow:
|
|
35
|
+
1. List scenarios to find the one you want to test
|
|
36
|
+
2. Run the scenario (optionally with --watch)
|
|
37
|
+
3. Check execution status with 'executions' or 'execution <id>'
|
|
38
|
+
4. Review history with 'history <scenarioId>'`
|
|
39
|
+
);
|
|
40
|
+
scenarios.command("list").description("List all scenarios").option("-s, --status <status>", "Filter by status (draft, active, paused, completed, archived)").option("-c, --category <category>", "Filter by category").option("-l, --limit <number>", "Number of items per page", "20").option("-p, --page <number>", "Page number", "1").addHelpText(
|
|
41
|
+
"after",
|
|
42
|
+
`
|
|
43
|
+
Examples:
|
|
44
|
+
$ chanl scenarios list # List all scenarios
|
|
45
|
+
$ chanl scenarios list --status active # Only active scenarios
|
|
46
|
+
$ chanl scenarios list --category sales # Filter by category
|
|
47
|
+
$ chanl scenarios list --json # Output as JSON`
|
|
48
|
+
).action(handleScenariosList);
|
|
49
|
+
scenarios.command("get <id>").description("Get a scenario by ID").addHelpText(
|
|
50
|
+
"after",
|
|
51
|
+
`
|
|
52
|
+
Examples:
|
|
53
|
+
$ chanl scenarios get abc123 # Get scenario details
|
|
54
|
+
$ chanl scenarios get abc123 --json # Output as JSON`
|
|
55
|
+
).action(handleScenariosGet);
|
|
56
|
+
scenarios.command("run <id>").description("Execute a scenario").option("-a, --agent <agentId>", "Specific agent to run against").option("-m, --mode <mode>", "Simulation mode: text, websocket, phone", "websocket").option("-p, --phone <number>", "Phone number for phone mode (E.164 format)").option("-w, --watch", "Watch execution progress until completion").option("--dry-run", "Validate without actually executing").addHelpText(
|
|
57
|
+
"after",
|
|
58
|
+
`
|
|
59
|
+
Examples:
|
|
60
|
+
$ chanl scenarios run abc123 # Run with defaults
|
|
61
|
+
$ chanl scenarios run abc123 --watch # Run and watch progress
|
|
62
|
+
$ chanl scenarios run abc123 --agent agent_xyz # Run against specific agent
|
|
63
|
+
$ chanl scenarios run abc123 --mode phone --phone +14155551234
|
|
64
|
+
$ chanl scenarios run abc123 --dry-run # Validate only
|
|
65
|
+
|
|
66
|
+
Simulation Modes:
|
|
67
|
+
text Free, instant text-based testing
|
|
68
|
+
websocket Real WebSocket testing (requires provider API key)
|
|
69
|
+
phone Real phone call (requires provider + phone number)`
|
|
70
|
+
).action(handleScenariosRun);
|
|
71
|
+
scenarios.command("executions").description("List scenario executions").option("-s, --scenario <scenarioId>", "Filter by scenario ID").option("--status <status>", "Filter by status (queued, running, completed, failed, timeout, cancelled)").option("-l, --limit <number>", "Number of items", "10").option("-p, --page <number>", "Page number", "1").addHelpText(
|
|
72
|
+
"after",
|
|
73
|
+
`
|
|
74
|
+
Examples:
|
|
75
|
+
$ chanl scenarios executions # List recent executions
|
|
76
|
+
$ chanl scenarios executions --scenario abc123 # For specific scenario
|
|
77
|
+
$ chanl scenarios executions --status completed # Only completed
|
|
78
|
+
$ chanl scenarios executions --json # Output as JSON`
|
|
79
|
+
).action(handleScenariosExecutions);
|
|
80
|
+
scenarios.command("execution <executionId>").description("Get execution details").addHelpText(
|
|
81
|
+
"after",
|
|
82
|
+
`
|
|
83
|
+
Examples:
|
|
84
|
+
$ chanl scenarios execution exec_abc123 # Get execution details
|
|
85
|
+
$ chanl scenarios execution exec_abc123 --json`
|
|
86
|
+
).action(handleScenariosExecution);
|
|
87
|
+
scenarios.command("history <scenarioId>").description("Get simulation history for a scenario").option("-l, --limit <number>", "Number of simulations to show", "10").addHelpText(
|
|
88
|
+
"after",
|
|
89
|
+
`
|
|
90
|
+
Examples:
|
|
91
|
+
$ chanl scenarios history abc123 # Last 10 runs
|
|
92
|
+
$ chanl scenarios history abc123 --limit 5 # Last 5 runs
|
|
93
|
+
$ chanl scenarios history abc123 --json # Output as JSON`
|
|
94
|
+
).action(handleScenariosHistory);
|
|
95
|
+
scenarios.command("run-all").description("Run all scenarios for an agent and show aggregate results").requiredOption("-a, --agent <agentId>", "Agent ID to run scenarios against").option("--min-score <score>", "Minimum passing score (0-100)", "0").option("--parallel <count>", "Max concurrent executions", "3").option("-m, --mode <mode>", "Simulation mode: text, phone", "text").addHelpText(
|
|
96
|
+
"after",
|
|
97
|
+
`
|
|
98
|
+
Examples:
|
|
99
|
+
$ chanl scenarios run-all --agent agent_abc # Run all with defaults
|
|
100
|
+
$ chanl scenarios run-all --agent agent_abc --min-score 80 # Fail if any score < 80
|
|
101
|
+
$ chanl scenarios run-all --agent agent_abc --parallel 5 # Run 5 at a time
|
|
102
|
+
$ chanl scenarios run-all --agent agent_abc --json # Output as JSON
|
|
103
|
+
|
|
104
|
+
Exit Codes:
|
|
105
|
+
0 All scenarios passed (score >= min-score)
|
|
106
|
+
1 One or more scenarios failed`
|
|
107
|
+
).action(handleScenariosRunAll);
|
|
108
|
+
return scenarios;
|
|
109
|
+
}
|
|
110
|
+
async function handleScenariosList(options) {
|
|
111
|
+
const sdk = createSdk();
|
|
112
|
+
if (!sdk) return;
|
|
113
|
+
const spinner = ora("Fetching scenarios...").start();
|
|
114
|
+
try {
|
|
115
|
+
const response = await sdk.scenarios.list({
|
|
116
|
+
status: options.status,
|
|
117
|
+
category: options.category,
|
|
118
|
+
limit: parseInt(options.limit, 10),
|
|
119
|
+
page: parseInt(options.page, 10)
|
|
120
|
+
});
|
|
121
|
+
spinner.stop();
|
|
122
|
+
if (!response.success || !response.data) {
|
|
123
|
+
printError("Failed to fetch scenarios", response.message);
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const { scenarios, pagination } = response.data;
|
|
128
|
+
if (isJsonOutput()) {
|
|
129
|
+
printJson(response.data);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (scenarios.length === 0) {
|
|
133
|
+
printInfo("No scenarios found");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
printBlank();
|
|
137
|
+
printSimpleTable(
|
|
138
|
+
["Name", "Status", "Category", "Difficulty", "Created"],
|
|
139
|
+
scenarios.map((s) => [
|
|
140
|
+
s.name,
|
|
141
|
+
formatStatus(s.status),
|
|
142
|
+
s.category || "-",
|
|
143
|
+
s.difficulty || "-",
|
|
144
|
+
formatDate(s.createdAt)
|
|
145
|
+
])
|
|
146
|
+
);
|
|
147
|
+
printBlank();
|
|
148
|
+
printInfo(`Total: ${pagination.total} scenarios (Page ${pagination.page} of ${pagination.pages})`);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
spinner.fail("Failed to fetch scenarios");
|
|
151
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
152
|
+
printError("Error", message);
|
|
153
|
+
process.exitCode = 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function handleScenariosGet(id) {
|
|
157
|
+
const sdk = createSdk();
|
|
158
|
+
if (!sdk) return;
|
|
159
|
+
const spinner = ora("Fetching scenario...").start();
|
|
160
|
+
try {
|
|
161
|
+
const response = await sdk.scenarios.get(id);
|
|
162
|
+
spinner.stop();
|
|
163
|
+
if (!response.success || !response.data) {
|
|
164
|
+
printError("Failed to fetch scenario", response.message);
|
|
165
|
+
process.exitCode = 1;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (isJsonOutput()) {
|
|
169
|
+
printJson(response.data);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const { scenario } = response.data;
|
|
173
|
+
printBlank();
|
|
174
|
+
console.log(chalk.bold("Scenario Details:"));
|
|
175
|
+
console.log(` Name: ${scenario.name}`);
|
|
176
|
+
console.log(` ID: ${scenario.id}`);
|
|
177
|
+
console.log(` Status: ${formatStatus(scenario.status)}`);
|
|
178
|
+
console.log(` Category: ${scenario.category || "-"}`);
|
|
179
|
+
console.log(` Difficulty: ${scenario.difficulty || "-"}`);
|
|
180
|
+
console.log(` Created: ${formatDate(scenario.createdAt)}`);
|
|
181
|
+
if (scenario.description) {
|
|
182
|
+
console.log(` Description: ${scenario.description}`);
|
|
183
|
+
}
|
|
184
|
+
printBlank();
|
|
185
|
+
} catch (error) {
|
|
186
|
+
spinner.fail("Failed to fetch scenario");
|
|
187
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
188
|
+
printError("Error", message);
|
|
189
|
+
process.exitCode = 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function formatStatus(status) {
|
|
193
|
+
switch (status) {
|
|
194
|
+
case "active":
|
|
195
|
+
return chalk.green("active");
|
|
196
|
+
case "paused":
|
|
197
|
+
return chalk.yellow("paused");
|
|
198
|
+
case "completed":
|
|
199
|
+
return chalk.blue("completed");
|
|
200
|
+
case "archived":
|
|
201
|
+
return chalk.gray("archived");
|
|
202
|
+
case "draft":
|
|
203
|
+
default:
|
|
204
|
+
return chalk.gray(status || "draft");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function formatExecutionStatus(status) {
|
|
208
|
+
switch (status) {
|
|
209
|
+
case "completed":
|
|
210
|
+
return chalk.green("completed");
|
|
211
|
+
case "running":
|
|
212
|
+
return chalk.cyan("running");
|
|
213
|
+
case "queued":
|
|
214
|
+
return chalk.blue("queued");
|
|
215
|
+
case "failed":
|
|
216
|
+
return chalk.red("failed");
|
|
217
|
+
case "timeout":
|
|
218
|
+
return chalk.yellow("timeout");
|
|
219
|
+
case "cancelled":
|
|
220
|
+
return chalk.gray("cancelled");
|
|
221
|
+
default:
|
|
222
|
+
return chalk.gray(status || "unknown");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function formatScore(score) {
|
|
226
|
+
if (score === void 0 || score === null) return "-";
|
|
227
|
+
if (score >= 80) return chalk.green(`${score}%`);
|
|
228
|
+
if (score >= 60) return chalk.yellow(`${score}%`);
|
|
229
|
+
return chalk.red(`${score}%`);
|
|
230
|
+
}
|
|
231
|
+
function formatDuration(seconds) {
|
|
232
|
+
if (!seconds) return "-";
|
|
233
|
+
if (seconds < 60) return `${seconds.toFixed(1)}s`;
|
|
234
|
+
const mins = Math.floor(seconds / 60);
|
|
235
|
+
const secs = seconds % 60;
|
|
236
|
+
return `${mins}m ${secs.toFixed(0)}s`;
|
|
237
|
+
}
|
|
238
|
+
async function handleScenariosRun(id, options) {
|
|
239
|
+
const sdk = createSdk();
|
|
240
|
+
if (!sdk) return;
|
|
241
|
+
const spinner = ora("Starting scenario execution...").start();
|
|
242
|
+
try {
|
|
243
|
+
const response = await sdk.scenarios.run(id, {
|
|
244
|
+
agentId: options.agent,
|
|
245
|
+
simulationMode: options.mode,
|
|
246
|
+
phoneNumber: options.phone,
|
|
247
|
+
dryRun: options.dryRun
|
|
248
|
+
});
|
|
249
|
+
if (!response.success || !response.data) {
|
|
250
|
+
spinner.fail("Failed to start execution");
|
|
251
|
+
printError("Error", response.message || "Unknown error");
|
|
252
|
+
process.exitCode = 1;
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const { execution, executionId } = response.data;
|
|
256
|
+
const execId = executionId || execution?.id || execution?.executionId;
|
|
257
|
+
if (options.dryRun) {
|
|
258
|
+
spinner.succeed("Dry run validation passed");
|
|
259
|
+
printInfo("Scenario is valid and ready to execute");
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
spinner.succeed(`Execution started: ${execId}`);
|
|
263
|
+
if (isJsonOutput()) {
|
|
264
|
+
printJson(response.data);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (!options.watch) {
|
|
268
|
+
printBlank();
|
|
269
|
+
printLabel("Execution ID", execId || "N/A");
|
|
270
|
+
printLabel("Status", formatExecutionStatus(execution?.status));
|
|
271
|
+
printInfo(`Run 'chanl scenarios execution ${execId}' to check status`);
|
|
272
|
+
printBlank();
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
await watchExecution(sdk, execId, options.mode === "phone");
|
|
276
|
+
} catch (error) {
|
|
277
|
+
spinner.fail("Failed to execute scenario");
|
|
278
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
279
|
+
printError("Error", message);
|
|
280
|
+
process.exitCode = 1;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async function watchExecution(sdk, executionId, isPhoneMode = false) {
|
|
284
|
+
const startTime = /* @__PURE__ */ new Date();
|
|
285
|
+
let spinner = ora(
|
|
286
|
+
isPhoneMode ? `${chalk.red("LIVE")} \u2014 ${formatElapsed(startTime)} | Watching voice execution...` : "Watching execution..."
|
|
287
|
+
).start();
|
|
288
|
+
const live = sdk.scenarios.watchExecution(executionId);
|
|
289
|
+
let finalExecution;
|
|
290
|
+
let scorecardReceived = false;
|
|
291
|
+
live.on("status", (status) => {
|
|
292
|
+
if (isPhoneMode) {
|
|
293
|
+
spinner.text = `${chalk.red("LIVE")} \u2014 ${formatElapsed(startTime)} | Status: ${status}`;
|
|
294
|
+
} else {
|
|
295
|
+
spinner.text = `Status: ${status}`;
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
live.on("step", (step) => {
|
|
299
|
+
if (!isPhoneMode) {
|
|
300
|
+
spinner.stop();
|
|
301
|
+
const stepStatus = step.status === "completed" ? chalk.green("\u2713") : chalk.red("\u2717");
|
|
302
|
+
console.log(` ${stepStatus} Step ${step.stepId}: ${step.response?.slice(0, 80) || "-"}`);
|
|
303
|
+
if (step.toolCalls.length > 0) {
|
|
304
|
+
for (const tc of step.toolCalls) {
|
|
305
|
+
const tcIcon = tc.status === "success" ? chalk.green("\u2713") : chalk.red("\u2717");
|
|
306
|
+
const dur = tc.durationMs ? chalk.dim(` (${tc.durationMs}ms)`) : "";
|
|
307
|
+
console.log(` ${tcIcon} ${chalk.yellow(tc.name)}${dur}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
spinner.start();
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
live.on("transcript", (seg) => {
|
|
314
|
+
spinner.stop();
|
|
315
|
+
printSegment(seg);
|
|
316
|
+
spinner.start();
|
|
317
|
+
});
|
|
318
|
+
live.on("completed", (execution) => {
|
|
319
|
+
finalExecution = execution;
|
|
320
|
+
const status = execution.status;
|
|
321
|
+
if (status === "completed") {
|
|
322
|
+
spinner.succeed(
|
|
323
|
+
isPhoneMode ? `Execution complete \u2014 ${formatElapsed(startTime)}` : "Execution completed"
|
|
324
|
+
);
|
|
325
|
+
} else if (status === "failed") {
|
|
326
|
+
spinner.fail(
|
|
327
|
+
isPhoneMode ? `Execution failed \u2014 ${formatElapsed(startTime)}` : "Execution failed"
|
|
328
|
+
);
|
|
329
|
+
} else {
|
|
330
|
+
spinner.warn(`Execution ${status}`);
|
|
331
|
+
}
|
|
332
|
+
printBlank();
|
|
333
|
+
printExecutionDetails(execution);
|
|
334
|
+
if (isPhoneMode && execution.callDetails?.callId) {
|
|
335
|
+
spinner = ora("Checking for scorecard...").start();
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
live.on("scorecard", (results) => {
|
|
339
|
+
scorecardReceived = true;
|
|
340
|
+
spinner.stop();
|
|
341
|
+
displayScorecard(results);
|
|
342
|
+
printBlank();
|
|
343
|
+
});
|
|
344
|
+
live.on("timeout", () => {
|
|
345
|
+
spinner.warn("Timeout waiting for execution to complete");
|
|
346
|
+
printInfo(`Run 'chanl scenarios execution ${executionId}' to check status`);
|
|
347
|
+
});
|
|
348
|
+
live.on("error", (err) => {
|
|
349
|
+
spinner.fail("Error while watching");
|
|
350
|
+
printError("Error", err.message);
|
|
351
|
+
process.exitCode = 1;
|
|
352
|
+
});
|
|
353
|
+
await live.completed;
|
|
354
|
+
if (finalExecution && isPhoneMode && finalExecution.callDetails?.callId && !scorecardReceived) {
|
|
355
|
+
spinner.stop();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function printExecutionDetails(execution) {
|
|
359
|
+
console.log(chalk.bold("Execution Results:"));
|
|
360
|
+
console.log(` Execution ID: ${execution.id || execution.executionId}`);
|
|
361
|
+
console.log(` Scenario ID: ${execution.scenarioId}`);
|
|
362
|
+
console.log(` Status: ${formatExecutionStatus(execution.status)}`);
|
|
363
|
+
console.log(` Score: ${formatScore(execution.overallScore)}`);
|
|
364
|
+
console.log(` Duration: ${formatDuration(execution.duration)}`);
|
|
365
|
+
if (execution.metrics) {
|
|
366
|
+
console.log(chalk.bold("\n Metrics:"));
|
|
367
|
+
if (execution.metrics.responseTime !== void 0) {
|
|
368
|
+
console.log(` Response Time: ${execution.metrics.responseTime.toFixed(0)}ms`);
|
|
369
|
+
}
|
|
370
|
+
if (execution.metrics.accuracy !== void 0) {
|
|
371
|
+
console.log(` Accuracy: ${formatScore(execution.metrics.accuracy)}`);
|
|
372
|
+
}
|
|
373
|
+
if (execution.metrics.completion !== void 0) {
|
|
374
|
+
console.log(` Completion: ${formatScore(execution.metrics.completion)}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (execution.callDetails?.callId) {
|
|
378
|
+
console.log(chalk.bold("\n Call Details:"));
|
|
379
|
+
console.log(` Call ID: ${execution.callDetails.callId}`);
|
|
380
|
+
if (execution.callDetails.phoneNumber) {
|
|
381
|
+
console.log(` Phone: ${execution.callDetails.phoneNumber}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const allToolCalls = (execution.stepResults ?? []).flatMap((s) => s.toolCalls ?? []);
|
|
385
|
+
if (allToolCalls.length > 0) {
|
|
386
|
+
console.log(chalk.bold("\n Tool Calls:"));
|
|
387
|
+
printScenarioToolCalls(allToolCalls);
|
|
388
|
+
}
|
|
389
|
+
if (execution.errorMessages && execution.errorMessages.length > 0) {
|
|
390
|
+
console.log(chalk.bold("\n Errors:"));
|
|
391
|
+
execution.errorMessages.forEach((msg) => {
|
|
392
|
+
console.log(chalk.red(` - ${msg}`));
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
printBlank();
|
|
396
|
+
}
|
|
397
|
+
function printScenarioToolCalls(toolCalls) {
|
|
398
|
+
for (const tc of toolCalls) {
|
|
399
|
+
const statusIcon = tc.status === "success" ? chalk.green("\u2713") : tc.status === "error" ? chalk.red("\u2717") : tc.status === "timeout" ? chalk.yellow("\u23F1") : chalk.dim("\u2026");
|
|
400
|
+
const duration = tc.durationMs ? chalk.dim(` (${tc.durationMs}ms)`) : "";
|
|
401
|
+
const params = Object.keys(tc.parameters).length ? chalk.dim(` ${JSON.stringify(tc.parameters)}`) : "";
|
|
402
|
+
console.log(` ${statusIcon} ${chalk.yellow(tc.name)}${params}${duration}`);
|
|
403
|
+
if (tc.status === "error" && tc.error) {
|
|
404
|
+
console.log(` ${chalk.red(tc.error.message)}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async function handleScenariosExecutions(options) {
|
|
409
|
+
const sdk = createSdk();
|
|
410
|
+
if (!sdk) return;
|
|
411
|
+
const spinner = ora("Fetching executions...").start();
|
|
412
|
+
try {
|
|
413
|
+
const response = await sdk.scenarios.getExecutions({
|
|
414
|
+
scenarioId: options.scenario,
|
|
415
|
+
status: options.status,
|
|
416
|
+
limit: parseInt(options.limit, 10),
|
|
417
|
+
page: parseInt(options.page, 10)
|
|
418
|
+
});
|
|
419
|
+
spinner.stop();
|
|
420
|
+
if (!response.success || !response.data) {
|
|
421
|
+
printError("Failed to fetch executions", response.message);
|
|
422
|
+
process.exitCode = 1;
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const { executions, total } = response.data;
|
|
426
|
+
if (isJsonOutput()) {
|
|
427
|
+
printJson(response.data);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
if (executions.length === 0) {
|
|
431
|
+
printInfo("No executions found");
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
printBlank();
|
|
435
|
+
printSimpleTable(
|
|
436
|
+
["Execution ID", "Scenario", "Status", "Score", "Duration", "Started"],
|
|
437
|
+
executions.map((e) => [
|
|
438
|
+
(e.id || e.executionId || "").slice(-12),
|
|
439
|
+
e.scenarioId?.slice(-8) || "-",
|
|
440
|
+
formatExecutionStatus(e.status),
|
|
441
|
+
formatScore(e.overallScore),
|
|
442
|
+
formatDuration(e.duration),
|
|
443
|
+
e.startTime ? formatDate(e.startTime) : "-"
|
|
444
|
+
])
|
|
445
|
+
);
|
|
446
|
+
printBlank();
|
|
447
|
+
printInfo(`Total: ${total} executions`);
|
|
448
|
+
} catch (error) {
|
|
449
|
+
spinner.fail("Failed to fetch executions");
|
|
450
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
451
|
+
printError("Error", message);
|
|
452
|
+
process.exitCode = 1;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
async function handleScenariosExecution(executionId) {
|
|
456
|
+
const sdk = createSdk();
|
|
457
|
+
if (!sdk) return;
|
|
458
|
+
const spinner = ora("Fetching execution...").start();
|
|
459
|
+
try {
|
|
460
|
+
const response = await sdk.scenarios.getExecution(executionId);
|
|
461
|
+
spinner.stop();
|
|
462
|
+
if (!response.success || !response.data) {
|
|
463
|
+
printError("Failed to fetch execution", response.message);
|
|
464
|
+
process.exitCode = 1;
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (isJsonOutput()) {
|
|
468
|
+
printJson(response.data);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const { execution } = response.data;
|
|
472
|
+
printBlank();
|
|
473
|
+
printExecutionDetails(execution);
|
|
474
|
+
} catch (error) {
|
|
475
|
+
spinner.fail("Failed to fetch execution");
|
|
476
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
477
|
+
printError("Error", message);
|
|
478
|
+
process.exitCode = 1;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
async function handleScenariosHistory(scenarioId, options) {
|
|
482
|
+
const sdk = createSdk();
|
|
483
|
+
if (!sdk) return;
|
|
484
|
+
const spinner = ora("Fetching simulation history...").start();
|
|
485
|
+
try {
|
|
486
|
+
const response = await sdk.scenarios.getSimulations(
|
|
487
|
+
scenarioId,
|
|
488
|
+
parseInt(options.limit, 10)
|
|
489
|
+
);
|
|
490
|
+
spinner.stop();
|
|
491
|
+
if (!response.success || !response.data) {
|
|
492
|
+
printError("Failed to fetch history", response.message);
|
|
493
|
+
process.exitCode = 1;
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const { simulations } = response.data;
|
|
497
|
+
if (isJsonOutput()) {
|
|
498
|
+
printJson(response.data);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
if (simulations.length === 0) {
|
|
502
|
+
printInfo("No simulation history found for this scenario");
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
printBlank();
|
|
506
|
+
console.log(chalk.bold(`Simulation History for ${scenarioId}`));
|
|
507
|
+
console.log(chalk.dim("\u2500".repeat(60)));
|
|
508
|
+
printSimpleTable(
|
|
509
|
+
["#", "Status", "Score", "Duration", "Started"],
|
|
510
|
+
simulations.map((s, i) => [
|
|
511
|
+
(i + 1).toString(),
|
|
512
|
+
formatExecutionStatus(s.status),
|
|
513
|
+
formatScore(s.overallScore),
|
|
514
|
+
formatDuration(s.duration),
|
|
515
|
+
s.startTime ? formatDate(s.startTime) : "-"
|
|
516
|
+
])
|
|
517
|
+
);
|
|
518
|
+
printBlank();
|
|
519
|
+
printInfo(`Showing last ${simulations.length} simulations`);
|
|
520
|
+
} catch (error) {
|
|
521
|
+
spinner.fail("Failed to fetch history");
|
|
522
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
523
|
+
printError("Error", message);
|
|
524
|
+
process.exitCode = 1;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async function handleScenariosRunAll(options) {
|
|
528
|
+
const sdk = createSdk();
|
|
529
|
+
if (!sdk) return;
|
|
530
|
+
const minScore = parseInt(options.minScore, 10);
|
|
531
|
+
const parallel = parseInt(options.parallel, 10);
|
|
532
|
+
const spinner = ora(`Running all scenarios for agent ${options.agent}...`).start();
|
|
533
|
+
try {
|
|
534
|
+
const result = await sdk.scenarios.runAll({
|
|
535
|
+
agentId: options.agent,
|
|
536
|
+
minScore,
|
|
537
|
+
parallel,
|
|
538
|
+
mode: options.mode
|
|
539
|
+
});
|
|
540
|
+
spinner.stop();
|
|
541
|
+
if (isJsonOutput()) {
|
|
542
|
+
printJson(result);
|
|
543
|
+
if (!result.allPassed) {
|
|
544
|
+
process.exitCode = 1;
|
|
545
|
+
}
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
if (result.totalScenarios === 0) {
|
|
549
|
+
printInfo("No active scenarios found for this agent");
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
printBlank();
|
|
553
|
+
console.log(chalk.bold("Batch Scenario Results"));
|
|
554
|
+
console.log(chalk.dim("\u2500".repeat(60)));
|
|
555
|
+
printSimpleTable(
|
|
556
|
+
["Scenario", "Status", "Score", "Duration", "Tools", "Result"],
|
|
557
|
+
result.results.map((r) => [
|
|
558
|
+
r.scenarioName,
|
|
559
|
+
formatExecutionStatus(r.status),
|
|
560
|
+
formatScore(r.score),
|
|
561
|
+
formatDuration(r.duration),
|
|
562
|
+
r.toolCallCount.toString(),
|
|
563
|
+
r.passed ? chalk.green("PASS") : chalk.red("FAIL")
|
|
564
|
+
])
|
|
565
|
+
);
|
|
566
|
+
printBlank();
|
|
567
|
+
console.log(chalk.bold("Summary"));
|
|
568
|
+
console.log(` Total: ${result.totalScenarios}`);
|
|
569
|
+
console.log(` Passed: ${chalk.green(result.passed.toString())}`);
|
|
570
|
+
console.log(` Failed: ${result.failed > 0 ? chalk.red(result.failed.toString()) : "0"}`);
|
|
571
|
+
console.log(` Average Score: ${formatScore(result.averageScore)}`);
|
|
572
|
+
console.log(` Min Score: ${result.minScore}`);
|
|
573
|
+
printBlank();
|
|
574
|
+
if (result.allPassed) {
|
|
575
|
+
console.log(chalk.green(`All ${result.totalScenarios} scenarios passed`));
|
|
576
|
+
} else {
|
|
577
|
+
console.log(chalk.red(`${result.failed} of ${result.totalScenarios} scenarios failed`));
|
|
578
|
+
process.exitCode = 1;
|
|
579
|
+
}
|
|
580
|
+
printBlank();
|
|
581
|
+
} catch (error) {
|
|
582
|
+
spinner.fail("Failed to run scenarios");
|
|
583
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
584
|
+
printError("Error", message);
|
|
585
|
+
process.exitCode = 1;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
export {
|
|
589
|
+
createScenariosCommand
|
|
590
|
+
};
|
|
591
|
+
//# sourceMappingURL=scenarios.js.map
|