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