@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/calls.ts"],"sourcesContent":["import { Command } from 'commander';\nimport ora from 'ora';\nimport chalk from 'chalk';\nimport * as fs from 'fs';\nimport type {\n Call,\n CallScorecardResult,\n CallTranscript,\n CallImportOptions,\n AnalysisField,\n ToolCall,\n} 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 printSuccess,\n printWarning,\n} from '../utils/output.js';\n\n/**\n * Create the calls command group\n */\nexport function createCallsCommand(): Command {\n const calls = new Command('calls')\n .description('Import, view, and analyze call recordings')\n .addHelpText(\n 'after',\n `\nWhat are Calls?\n Calls are voice recordings from your AI agents (via VAPI, Twilio, etc.)\n or imported transcripts/recordings. Each call can be analyzed with\n scorecards to evaluate agent performance.\n\nQuick Start:\n $ chanl calls import --transcript \"...\" # Import a call\n $ chanl calls list # List recent calls\n $ chanl calls list --external-ref orderId=123 # Filter by external ref\n $ chanl calls get <id> # View call details\n $ chanl calls analyze <id> # Trigger scorecard analysis\n $ chanl calls delete <id> # Delete a call\n\nImport Workflow:\n 1. Import a call with transcript, audio URL, or S3\n 2. Optionally specify analysis fields and scorecard\n 3. Webhook notifies when analysis is complete\n 4. View results with 'analysis <id>'\n\nAnalysis Workflow:\n 1. List calls to find one to analyze\n 2. Trigger analysis with 'analyze <id>'\n 3. View results with 'analysis <id>'\n 4. Check transcript with 'transcript <id>'`\n );\n\n // calls list\n calls\n .command('list')\n .description('List calls')\n .option('-s, --status <status>', 'Filter by status (pending, in_progress, completed, failed)')\n .option('-a, --agent <agentId>', 'Filter by agent ID')\n .option('-d, --direction <direction>', 'Filter by direction (inbound, outbound)')\n .option('--customer <name>', 'Filter by customer name')\n .option('-e, --external-ref <key=value>', 'Filter by external reference (repeatable)', collectExternalRefs, {})\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 calls list # List recent calls\n $ chanl calls list --status completed # Only completed calls\n $ chanl calls list --agent abc123 # Filter by agent\n $ chanl calls list --external-ref orderId=ORDER-123 # Filter by external ref\n $ chanl calls list -e orderId=123 -e customerId=456 # Multiple external refs\n $ chanl calls list --json # Output as JSON`\n )\n .action(handleCallsList);\n\n // calls get <id>\n calls\n .command('get <id>')\n .description('Get call details')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl calls get abc123 # Get call details\n $ chanl calls get abc123 --json # Output as JSON`\n )\n .action(handleCallsGet);\n\n // calls analyze <id>\n calls\n .command('analyze <id>')\n .description('Trigger scorecard analysis for a call')\n .option('-s, --scorecard <scorecardId>', 'Specific scorecard to use')\n .option('-f, --force', 'Force re-analysis even if already done')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl calls analyze abc123 # Analyze with default scorecard\n $ chanl calls analyze abc123 --scorecard xyz # Use specific scorecard\n $ chanl calls analyze abc123 --force # Force re-analysis`\n )\n .action(handleCallsAnalyze);\n\n // calls analysis <id>\n calls\n .command('analysis <id>')\n .description('View scorecard analysis results for a call')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl calls analysis abc123 # View analysis results\n $ chanl calls analysis abc123 --json # Output as JSON`\n )\n .action(handleCallsAnalysis);\n\n // calls transcript <id>\n calls\n .command('transcript <id>')\n .description('View call transcript')\n .option('--full', 'Show full transcript text instead of segments')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl calls transcript abc123 # View transcript segments\n $ chanl calls transcript abc123 --full # Show full text\n $ chanl calls transcript abc123 --json # Output as JSON`\n )\n .action(handleCallsTranscript);\n\n // calls metrics <id>\n calls\n .command('metrics <id>')\n .description('View call metrics')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl calls metrics abc123 # View call metrics\n $ chanl calls metrics abc123 --json # Output as JSON`\n )\n .action(handleCallsMetrics);\n\n // calls import\n calls\n .command('import')\n .description('Import a call from transcript, audio URL, or S3')\n .option('-t, --transcript <text>', 'Transcript text (plain text or JSON segments)')\n .option('-f, --file <path>', 'JSON file with import options')\n .option('--audio-url <url>', 'Audio file URL to transcribe')\n .option('--audio-id <id>', 'Pre-uploaded audio ID')\n .option('--bucket <name>', 'S3 bucket name')\n .option('--key <key>', 'S3 object key')\n .option('--region <region>', 'AWS region')\n .option('--customer-name <name>', 'Customer name')\n .option('--agent-name <name>', 'Agent name')\n .option('--direction <dir>', 'Call direction (inbound, outbound)')\n .option('-e, --external-ref <key=value>', 'External reference (repeatable)', collectExternalRefs, {})\n .option('--analysis-fields <fields>', 'Comma-separated analysis fields')\n .option('--scorecard <id>', 'Scorecard ID to evaluate against')\n .option('--webhook-url <url>', 'Webhook URL for completion notification')\n .addHelpText(\n 'after',\n `\nImport Modes (at least one required):\n --transcript Import with existing transcript text\n --audio-url Import from audio URL (will be transcribed)\n --audio-id Import using pre-uploaded audio\n --bucket/key Import from S3 bucket\n --file Import from JSON file containing all options\n\nAnalysis Fields:\n sentiment, topics, keywords, quality, coaching, predictions,\n speakers, upsell, metrics, extraction, followups\n\nExamples:\n # Import with transcript\n $ chanl calls import --transcript \"Agent: Hello! Customer: Hi...\"\n\n # Import with customer metadata\n $ chanl calls import --transcript \"...\" --customer-name \"John\" -e orderId=ORD-123\n\n # Import from audio URL\n $ chanl calls import --audio-url \"https://example.com/call.mp3\"\n\n # Import from JSON file\n $ chanl calls import -f import-data.json\n\n # Import with analysis options\n $ chanl calls import -t \"...\" --scorecard abc123 --analysis-fields sentiment,quality`\n )\n .action(handleCallsImport);\n\n // calls delete <id>\n calls\n .command('delete <id>')\n .description('Delete a call')\n .option('-y, --yes', 'Skip confirmation prompt')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl calls delete abc123 # Delete with confirmation\n $ chanl calls delete abc123 --yes # Delete without confirmation`\n )\n .action(handleCallsDelete);\n\n // calls bulk-delete\n calls\n .command('bulk-delete')\n .description('Delete multiple calls')\n .option('--ids <ids>', 'Comma-separated call IDs to delete')\n .option('-f, --file <path>', 'File with call IDs (one per line)')\n .option('-y, --yes', 'Skip confirmation prompt')\n .option('--keep-files', 'Do not delete associated storage files')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl calls bulk-delete --ids id1,id2,id3 # Delete by IDs\n $ chanl calls bulk-delete -f call-ids.txt # Delete from file\n $ chanl calls bulk-delete --ids id1,id2 --yes # Skip confirmation`\n )\n .action(handleCallsBulkDelete);\n\n return calls;\n}\n\n/**\n * Format call status with color\n */\nfunction formatStatus(status?: string): string {\n switch (status) {\n case 'completed':\n return chalk.green('completed');\n case 'in_progress':\n return chalk.cyan('in_progress');\n case 'pending':\n return chalk.blue('pending');\n case 'failed':\n return chalk.red('failed');\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 `${Math.round(seconds)}s`;\n const mins = Math.floor(seconds / 60);\n const secs = Math.round(seconds % 60);\n return `${mins}m ${secs}s`;\n}\n\n/**\n * Collect external refs from repeatable --external-ref options\n * Parses key=value pairs into a Record<string, string>\n */\nfunction collectExternalRefs(\n value: string,\n previous: Record<string, string>\n): Record<string, string> {\n const [key, ...rest] = value.split('=');\n if (!key || rest.length === 0) {\n printWarning(`Invalid external-ref format: '${value}'. Expected key=value`);\n return previous;\n }\n return { ...previous, [key]: rest.join('=') };\n}\n\n/**\n * Handle calls list command\n */\nasync function handleCallsList(options: {\n status?: string;\n agent?: string;\n direction?: string;\n customer?: string;\n externalRef?: Record<string, string>;\n limit: string;\n page: string;\n}): Promise<void> {\n const sdk = createSdk();\n if (!sdk) return;\n\n const spinner = ora('Fetching calls...').start();\n\n try {\n const response = await sdk.calls.list({\n status: options.status,\n agentId: options.agent,\n direction: options.direction as 'inbound' | 'outbound' | undefined,\n customerName: options.customer,\n externalRefs: options.externalRef && Object.keys(options.externalRef).length > 0\n ? options.externalRef\n : undefined,\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 calls', response.message);\n process.exitCode = 1;\n return;\n }\n\n // response.data may be an array (from API normalization) or { calls, pagination }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const rawData = response.data as any;\n const calls: Call[] = Array.isArray(rawData)\n ? rawData\n : rawData.calls || rawData.interactions || [];\n\n if (isJsonOutput()) {\n printJson(response.data);\n return;\n }\n\n if (calls.length === 0) {\n printInfo('No calls found');\n return;\n }\n\n printBlank();\n printSimpleTable(\n ['ID', 'Status', 'Duration', 'Score', 'Agent', 'Created'],\n calls.map((c: Call) => [\n c.id.slice(-12),\n formatStatus(c.status),\n formatDuration(c.duration),\n formatScore(c.score),\n c.agentId?.slice(-8) || '-',\n formatDate(c.createdAt),\n ])\n );\n\n printBlank();\n printInfo(`${calls.length} calls shown`);\n } catch (error) {\n spinner.fail('Failed to fetch calls');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\n/**\n * Handle calls get command\n */\nasync function handleCallsGet(id: string): Promise<void> {\n const sdk = createSdk();\n if (!sdk) return;\n\n const spinner = ora('Fetching call...').start();\n\n try {\n const response = await sdk.calls.get(id);\n\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Failed to fetch call', response.message);\n process.exitCode = 1;\n return;\n }\n\n if (isJsonOutput()) {\n printJson(response.data);\n return;\n }\n\n // response.data is the call object (unwrapped by SDK client)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const call = (response.data as any).call || response.data as any;\n printBlank();\n console.log(chalk.bold('Call Details:'));\n console.log(` ID: ${call.id}`);\n console.log(` Status: ${formatStatus(call.status)}`);\n console.log(` Duration: ${formatDuration(call.duration)}`);\n console.log(` Score: ${formatScore(call.score)}`);\n if (call.agentId) console.log(` Agent: ${call.agentId}`);\n if (call.scenarioId) console.log(` Scenario: ${call.scenarioId}`);\n console.log(` Created: ${formatDate(call.createdAt)}`);\n printBlank();\n } catch (error) {\n spinner.fail('Failed to fetch call');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\n/**\n * Handle calls analyze command\n */\nasync function handleCallsAnalyze(\n id: string,\n options: { scorecard?: string; force?: boolean }\n): Promise<void> {\n const sdk = createSdk();\n if (!sdk) return;\n\n const spinner = ora('Triggering analysis...').start();\n\n try {\n const response = await sdk.calls.analyze(id, {\n scorecardId: options.scorecard,\n forceReanalysis: options.force,\n });\n\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Failed to trigger analysis', response.message);\n process.exitCode = 1;\n return;\n }\n\n if (isJsonOutput()) {\n printJson(response.data);\n return;\n }\n\n const { status, resultId, message } = response.data;\n\n printBlank();\n if (status === 'queued') {\n console.log(chalk.green('✓ Analysis queued successfully'));\n if (resultId) {\n printLabel('Result ID', resultId);\n }\n printInfo(`Run 'chanl calls analysis ${id}' to view results when complete`);\n } else if (status === 'no_scorecard') {\n console.log(chalk.yellow('⚠ No scorecard available'));\n printInfo(message || 'Configure a default scorecard for your workspace');\n } else if (status === 'no_transcript') {\n console.log(chalk.yellow('⚠ No transcript available'));\n printInfo(message || 'Call needs a transcript before analysis');\n }\n printBlank();\n } catch (error) {\n spinner.fail('Failed to trigger analysis');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\n/**\n * Handle calls analysis command\n */\nasync function handleCallsAnalysis(id: string): Promise<void> {\n const sdk = createSdk();\n if (!sdk) return;\n\n const spinner = ora('Fetching analysis...').start();\n\n try {\n const response = await sdk.calls.getAnalysis(id);\n\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Failed to fetch analysis', response.message);\n process.exitCode = 1;\n return;\n }\n\n if (isJsonOutput()) {\n printJson(response.data);\n return;\n }\n\n // SDK may unwrap { results: [...] } → the array directly\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const results: any[] | undefined = Array.isArray(response.data)\n ? response.data\n : (response.data as any).results;\n\n if (!results || results.length === 0) {\n printInfo('No analysis results found for this call');\n printInfo(`Run 'chanl calls analyze ${id}' to trigger analysis`);\n return;\n }\n\n printBlank();\n console.log(chalk.bold(`Analysis Results for Call ${id.slice(-12)}`));\n console.log(chalk.dim('─'.repeat(60)));\n\n for (const result of results as CallScorecardResult[]) {\n console.log(chalk.bold(`\\nScorecard: ${result.scorecardId?.slice(-8) || 'Unknown'}`));\n console.log(` Overall Score: ${formatScore(result.overallScore)}`);\n console.log(` Status: ${result.status || 'completed'}`);\n\n if (result.categoryScores && result.categoryScores.length > 0) {\n console.log(chalk.bold('\\n Category Scores:'));\n for (const cat of result.categoryScores) {\n console.log(` ${cat.categoryName}: ${formatScore(cat.score)}`);\n }\n }\n\n if (result.feedback) {\n console.log(chalk.bold('\\n Feedback:'));\n console.log(` ${result.feedback}`);\n }\n }\n\n printBlank();\n } catch (error) {\n spinner.fail('Failed to fetch analysis');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\n/**\n * Handle calls transcript command\n */\nasync function handleCallsTranscript(\n id: string,\n options: { full?: boolean }\n): Promise<void> {\n const sdk = createSdk();\n if (!sdk) return;\n\n const spinner = ora('Fetching transcript...').start();\n\n try {\n const response = await sdk.calls.getTranscript(id);\n\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Failed to fetch transcript', response.message);\n process.exitCode = 1;\n return;\n }\n\n if (isJsonOutput()) {\n printJson(response.data);\n return;\n }\n\n // SDK unwraps { transcript: {...} } → the transcript object directly\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const transcript = (response.data as any).transcript || response.data as unknown as CallTranscript | null;\n\n if (!transcript) {\n printInfo('No transcript available for this call');\n return;\n }\n\n printBlank();\n console.log(chalk.bold(`Transcript for Call ${id.slice(-12)}`));\n console.log(chalk.dim('─'.repeat(60)));\n\n if (transcript.wordCount) {\n printLabel('Word Count', transcript.wordCount.toString());\n }\n if (transcript.speakerCount) {\n printLabel('Speakers', transcript.speakerCount.toString());\n }\n if (transcript.language) {\n printLabel('Language', transcript.language);\n }\n if (transcript.confidence) {\n printLabel('Confidence', `${(transcript.confidence * 100).toFixed(1)}%`);\n }\n\n console.log(chalk.dim('─'.repeat(60)));\n\n if (options.full || !transcript.segments || transcript.segments.length === 0) {\n console.log('\\n' + (transcript.text || 'No text available'));\n } else {\n console.log('');\n for (const segment of transcript.segments) {\n const speaker = segment.speaker === 'speaker_0' ? chalk.cyan('Agent') : chalk.yellow('Customer');\n const time = `[${formatDuration(segment.start)} - ${formatDuration(segment.end)}]`;\n console.log(`${speaker} ${chalk.dim(time)}`);\n console.log(` ${segment.text}`);\n console.log('');\n }\n }\n\n // Display tool calls if present\n if (transcript.toolCalls && transcript.toolCalls.length > 0) {\n console.log(chalk.dim('─'.repeat(60)));\n console.log(chalk.bold(`Tool Calls (${transcript.toolCalls.length}):`));\n console.log('');\n printTranscriptToolCalls(transcript.toolCalls);\n }\n\n printBlank();\n } catch (error) {\n spinner.fail('Failed to fetch transcript');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\n/**\n * Handle calls metrics command\n */\nasync function handleCallsMetrics(id: string): Promise<void> {\n const sdk = createSdk();\n if (!sdk) return;\n\n const spinner = ora('Fetching metrics...').start();\n\n try {\n const response = await sdk.calls.getMetrics(id);\n\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Failed to fetch metrics', response.message);\n process.exitCode = 1;\n return;\n }\n\n if (isJsonOutput()) {\n printJson(response.data);\n return;\n }\n\n // SDK unwraps { metrics: {...} } → the metrics object directly\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const metrics: any = (response.data as any).metrics || response.data;\n\n printBlank();\n console.log(chalk.bold(`Metrics for Call ${id.slice(-12)}`));\n console.log(chalk.dim('─'.repeat(50)));\n\n if (metrics.duration) {\n printLabel('Duration', formatDuration(metrics.duration));\n }\n\n if (metrics.talkTime) {\n console.log(chalk.bold('\\nTalk Time:'));\n if (metrics.talkTime.total) console.log(` Total: ${formatDuration(metrics.talkTime.total)}`);\n if (metrics.talkTime.agent) console.log(` Agent: ${formatDuration(metrics.talkTime.agent)}`);\n if (metrics.talkTime.customer) console.log(` Customer: ${formatDuration(metrics.talkTime.customer)}`);\n }\n\n if (metrics.responseTime) {\n console.log(chalk.bold('\\nResponse Time:'));\n if (metrics.responseTime.average) console.log(` Average: ${metrics.responseTime.average.toFixed(0)}ms`);\n if (metrics.responseTime.max) console.log(` Max: ${metrics.responseTime.max.toFixed(0)}ms`);\n }\n\n if (metrics.silence) {\n console.log(chalk.bold('\\nSilence:'));\n if (metrics.silence.total) console.log(` Total: ${formatDuration(metrics.silence.total)}`);\n if (metrics.silence.count) console.log(` Count: ${metrics.silence.count} pauses`);\n }\n\n if (metrics.interruptions) {\n console.log(chalk.bold('\\nInterruptions:'));\n if (metrics.interruptions.total !== undefined) console.log(` Total: ${metrics.interruptions.total}`);\n if (metrics.interruptions.byAgent !== undefined) console.log(` By Agent: ${metrics.interruptions.byAgent}`);\n if (metrics.interruptions.byCustomer !== undefined) console.log(` By Customer: ${metrics.interruptions.byCustomer}`);\n }\n\n if (metrics.turnCount) {\n printLabel('\\nTurn Count', metrics.turnCount.toString());\n }\n\n printBlank();\n } catch (error) {\n spinner.fail('Failed to fetch metrics');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\n/**\n * Handle calls import command\n */\nasync function handleCallsImport(options: {\n transcript?: string;\n file?: string;\n audioUrl?: string;\n audioId?: string;\n bucket?: string;\n key?: string;\n region?: string;\n customerName?: string;\n agentName?: string;\n direction?: string;\n externalRef?: Record<string, string>;\n analysisFields?: string;\n scorecard?: string;\n webhookUrl?: string;\n}): Promise<void> {\n const sdk = createSdk();\n if (!sdk) return;\n\n // Validate that at least one import mode is provided\n const hasTranscript = !!options.transcript;\n const hasFile = !!options.file;\n const hasAudioUrl = !!options.audioUrl;\n const hasAudioId = !!options.audioId;\n const hasS3 = !!(options.bucket && options.key);\n\n if (!hasTranscript && !hasFile && !hasAudioUrl && !hasAudioId && !hasS3) {\n printError(\n 'Missing import source',\n 'Provide one of: --transcript, --file, --audio-url, --audio-id, or --bucket/--key'\n );\n process.exitCode = 1;\n return;\n }\n\n let importOptions: CallImportOptions;\n\n // If file is provided, read it and use its contents\n if (options.file) {\n try {\n const fileContent = fs.readFileSync(options.file, 'utf-8');\n importOptions = JSON.parse(fileContent) as CallImportOptions;\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Unknown error';\n printError('Failed to read import file', message);\n process.exitCode = 1;\n return;\n }\n } else {\n // Build import options from CLI arguments\n importOptions = {};\n\n // Transcript (either plain text or JSON segments)\n if (options.transcript) {\n try {\n // Try parsing as JSON first (for segment array)\n const parsed = JSON.parse(options.transcript);\n if (Array.isArray(parsed)) {\n importOptions.transcript = parsed;\n } else {\n importOptions.transcript = options.transcript;\n }\n } catch {\n // Not JSON, use as plain text\n importOptions.transcript = options.transcript;\n }\n }\n\n // Audio options\n if (options.audioUrl) {\n importOptions.audioUrl = options.audioUrl;\n }\n if (options.audioId) {\n importOptions.audioId = options.audioId;\n }\n\n // S3 options\n if (options.bucket) {\n importOptions.bucket = options.bucket;\n }\n if (options.key) {\n importOptions.key = options.key;\n }\n if (options.region) {\n importOptions.region = options.region;\n }\n\n // Metadata\n if (options.customerName) {\n importOptions.customerName = options.customerName;\n }\n if (options.agentName) {\n importOptions.agentName = options.agentName;\n }\n if (options.direction) {\n importOptions.callDirection = options.direction as 'inbound' | 'outbound';\n }\n\n // External references\n if (options.externalRef && Object.keys(options.externalRef).length > 0) {\n importOptions.externalReferenceIds = options.externalRef;\n }\n\n // Analysis options\n if (options.analysisFields) {\n importOptions.analysisFields = options.analysisFields.split(',').map(f => f.trim()) as AnalysisField[];\n }\n if (options.scorecard) {\n importOptions.scorecardId = options.scorecard;\n }\n\n // Webhook\n if (options.webhookUrl) {\n importOptions.webhookUrl = options.webhookUrl;\n }\n }\n\n const spinner = ora('Importing call...').start();\n\n try {\n const response = await sdk.calls.import(importOptions);\n\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Failed to import call', response.message);\n process.exitCode = 1;\n return;\n }\n\n if (isJsonOutput()) {\n printJson(response.data);\n return;\n }\n\n const { callId, processingStatus, importMode, externalReferenceIds } = response.data;\n\n printBlank();\n printSuccess('Call imported successfully');\n printLabel('Call ID', callId);\n printLabel('Status', processingStatus || 'pending');\n if (importMode) {\n printLabel('Import Mode', importMode);\n }\n if (externalReferenceIds && Object.keys(externalReferenceIds).length > 0) {\n printLabel('External Refs', Object.entries(externalReferenceIds).map(([k, v]) => `${k}=${v}`).join(', '));\n }\n printBlank();\n printInfo(`Run 'chanl calls get ${callId}' to view call details`);\n printBlank();\n } catch (error) {\n spinner.fail('Failed to import call');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\n/**\n * Handle calls delete command\n */\nasync function handleCallsDelete(\n id: string,\n options: { yes?: boolean }\n): Promise<void> {\n const sdk = createSdk();\n if (!sdk) return;\n\n // Confirmation prompt\n if (!options.yes) {\n printWarning(`This will permanently delete call ${id.slice(-12)}`);\n printInfo('Use --yes to skip this confirmation');\n\n // Simple readline-based confirmation\n const readline = await import('readline');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const answer = await new Promise<string>((resolve) => {\n rl.question(chalk.yellow('Are you sure? (y/N): '), (ans) => {\n rl.close();\n resolve(ans);\n });\n });\n\n if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {\n printInfo('Cancelled');\n return;\n }\n }\n\n const spinner = ora('Deleting call...').start();\n\n try {\n const response = await sdk.calls.delete(id);\n\n spinner.stop();\n\n if (!response.success) {\n printError('Failed to delete call', response.message);\n process.exitCode = 1;\n return;\n }\n\n if (isJsonOutput()) {\n printJson({ deleted: true, callId: id });\n return;\n }\n\n printSuccess(`Call ${id.slice(-12)} deleted successfully`);\n } catch (error) {\n spinner.fail('Failed to delete call');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\n/**\n * Handle calls bulk-delete command\n */\nasync function handleCallsBulkDelete(options: {\n ids?: string;\n file?: string;\n yes?: boolean;\n keepFiles?: boolean;\n}): Promise<void> {\n const sdk = createSdk();\n if (!sdk) return;\n\n // Collect IDs from options\n let callIds: string[] = [];\n\n if (options.ids) {\n callIds = options.ids.split(',').map(id => id.trim()).filter(Boolean);\n }\n\n if (options.file) {\n try {\n const fileContent = fs.readFileSync(options.file, 'utf-8');\n const fileIds = fileContent\n .split('\\n')\n .map(line => line.trim())\n .filter(line => line && !line.startsWith('#'));\n callIds = [...callIds, ...fileIds];\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Unknown error';\n printError('Failed to read IDs file', message);\n process.exitCode = 1;\n return;\n }\n }\n\n // Remove duplicates\n callIds = [...new Set(callIds)];\n\n if (callIds.length === 0) {\n printError('No call IDs provided', 'Use --ids or --file to specify calls to delete');\n process.exitCode = 1;\n return;\n }\n\n // Confirmation prompt\n if (!options.yes) {\n printWarning(`This will permanently delete ${callIds.length} call(s)`);\n printInfo('Use --yes to skip this confirmation');\n\n const readline = await import('readline');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const answer = await new Promise<string>((resolve) => {\n rl.question(chalk.yellow('Are you sure? (y/N): '), (ans) => {\n rl.close();\n resolve(ans);\n });\n });\n\n if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {\n printInfo('Cancelled');\n return;\n }\n }\n\n const spinner = ora(`Deleting ${callIds.length} calls...`).start();\n\n try {\n const response = await sdk.calls.bulkDelete({\n callIds,\n deleteStorageFiles: !options.keepFiles,\n });\n\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Failed to bulk delete calls', response.message);\n process.exitCode = 1;\n return;\n }\n\n if (isJsonOutput()) {\n printJson(response.data);\n return;\n }\n\n const { deletedCount, failedIds, storageFilesDeleted } = response.data;\n\n printBlank();\n printSuccess(`Successfully deleted ${deletedCount} call(s)`);\n if (storageFilesDeleted > 0) {\n printInfo(`Deleted ${storageFilesDeleted} storage files`);\n }\n if (failedIds && failedIds.length > 0) {\n printWarning(`Failed to delete ${failedIds.length} call(s): ${failedIds.slice(0, 5).join(', ')}${failedIds.length > 5 ? '...' : ''}`);\n }\n printBlank();\n } catch (error) {\n spinner.fail('Failed to bulk delete calls');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\n/**\n * Print tool calls from a call transcript\n */\nfunction printTranscriptToolCalls(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 time = tc.secondsFromStart !== undefined\n ? chalk.dim(` @ ${tc.secondsFromStart.toFixed(1)}s`)\n : '';\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}${time}`);\n\n if (tc.status === 'error' && tc.error) {\n console.log(` ${chalk.red(tc.error.message)}`);\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { createInterface } from "node:readline";
|
|
5
|
+
import { createSdk } from "../utils/sdk-factory.js";
|
|
6
|
+
import {
|
|
7
|
+
printError,
|
|
8
|
+
printSuccess,
|
|
9
|
+
printInfo,
|
|
10
|
+
printBlank,
|
|
11
|
+
isJsonOutput,
|
|
12
|
+
printJson
|
|
13
|
+
} from "../utils/output.js";
|
|
14
|
+
function createChatCommand() {
|
|
15
|
+
const chat = new Command("chat").description("Start an interactive chat session with an agent").argument("<agent-id>", "ID of the agent to chat with").option("-m, --message <text>", "Send a single message and exit").option("-s, --stream", "Enable streaming responses (real-time text)").option("-w, --workspace <id>", "Workspace ID override").addHelpText(
|
|
16
|
+
"after",
|
|
17
|
+
`
|
|
18
|
+
What is Chat?
|
|
19
|
+
Chat lets you test an AI agent via text in your terminal. It creates a session
|
|
20
|
+
through interactions-service which manages the full lifecycle including
|
|
21
|
+
transcription, billing, and evaluation.
|
|
22
|
+
|
|
23
|
+
Modes:
|
|
24
|
+
Interactive (default): Opens a prompt loop \u2014 type messages, see agent responses.
|
|
25
|
+
Type 'exit', 'quit', or press Ctrl+C to end.
|
|
26
|
+
|
|
27
|
+
Single message: Send one message with --message, print response, exit.
|
|
28
|
+
|
|
29
|
+
Streaming:
|
|
30
|
+
Use --stream for real-time text output. Text appears as the agent generates it.
|
|
31
|
+
Note: streaming responses do not include tool call metadata.
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
$ chanl chat abc123 # Interactive chat
|
|
35
|
+
$ chanl chat abc123 --stream # Interactive with streaming
|
|
36
|
+
$ chanl chat abc123 --message "Hello" # Single message mode
|
|
37
|
+
$ chanl chat abc123 --message "Hi" --stream # Single message, streamed
|
|
38
|
+
$ chanl chat abc123 --message "Hi" --json # Single message, JSON output
|
|
39
|
+
$ chanl chat abc123 --workspace ws_456 # Override workspace`
|
|
40
|
+
).action(handleChat);
|
|
41
|
+
return chat;
|
|
42
|
+
}
|
|
43
|
+
async function handleChat(agentId, options) {
|
|
44
|
+
const sdk = createSdk();
|
|
45
|
+
if (!sdk) return;
|
|
46
|
+
if (options.message) {
|
|
47
|
+
await handleSingleMessage(sdk, agentId, options.message, options.stream);
|
|
48
|
+
} else {
|
|
49
|
+
await handleInteractiveChat(sdk, agentId, options.stream);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function handleSingleMessage(sdk, agentId, message, stream) {
|
|
53
|
+
const spinner = ora("Connecting to agent...").start();
|
|
54
|
+
try {
|
|
55
|
+
const chat = await sdk.chat.session(agentId);
|
|
56
|
+
spinner.text = "Sending message...";
|
|
57
|
+
try {
|
|
58
|
+
if (stream && !isJsonOutput()) {
|
|
59
|
+
spinner.stop();
|
|
60
|
+
printBlank();
|
|
61
|
+
console.log(chalk.dim("You >"), message);
|
|
62
|
+
process.stdout.write(chalk.cyan("Agent > "));
|
|
63
|
+
const reply = await chat.send(message, {
|
|
64
|
+
stream: true,
|
|
65
|
+
onChunk: (delta) => process.stdout.write(delta)
|
|
66
|
+
});
|
|
67
|
+
console.log();
|
|
68
|
+
if (reply.latencyMs) {
|
|
69
|
+
printBlank();
|
|
70
|
+
printInfo(`Response time: ${reply.latencyMs}ms`);
|
|
71
|
+
}
|
|
72
|
+
printBlank();
|
|
73
|
+
} else {
|
|
74
|
+
const reply = await chat.send(message);
|
|
75
|
+
spinner.stop();
|
|
76
|
+
if (isJsonOutput()) {
|
|
77
|
+
printJson({
|
|
78
|
+
sessionId: chat.sessionId,
|
|
79
|
+
agentId,
|
|
80
|
+
message,
|
|
81
|
+
response: reply.message,
|
|
82
|
+
toolCalls: reply.toolCalls,
|
|
83
|
+
latencyMs: reply.latencyMs
|
|
84
|
+
});
|
|
85
|
+
} else {
|
|
86
|
+
printBlank();
|
|
87
|
+
console.log(chalk.dim("You >"), message);
|
|
88
|
+
if (reply.toolCalls?.length) {
|
|
89
|
+
console.log(chalk.dim(" Tools:"));
|
|
90
|
+
printToolCalls(reply.toolCalls);
|
|
91
|
+
}
|
|
92
|
+
console.log(chalk.cyan("Agent >"), reply.message);
|
|
93
|
+
if (reply.latencyMs) {
|
|
94
|
+
printBlank();
|
|
95
|
+
printInfo(`Response time: ${reply.latencyMs}ms`);
|
|
96
|
+
}
|
|
97
|
+
printBlank();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} finally {
|
|
101
|
+
await chat.end();
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
spinner.fail("Chat failed");
|
|
105
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
106
|
+
printError("Error", errorMessage);
|
|
107
|
+
process.exitCode = 1;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function handleInteractiveChat(sdk, agentId, stream) {
|
|
111
|
+
const spinner = ora("Connecting to agent...").start();
|
|
112
|
+
try {
|
|
113
|
+
const chat = await sdk.chat.session(agentId);
|
|
114
|
+
const agentName = chat.agentName || "Agent";
|
|
115
|
+
spinner.succeed(`Connected to ${agentName}${stream ? " (streaming)" : ""}`);
|
|
116
|
+
printInfo("Type 'exit' or 'quit' to end the session. Ctrl+C also works.");
|
|
117
|
+
printBlank();
|
|
118
|
+
const rl = createInterface({
|
|
119
|
+
input: process.stdin,
|
|
120
|
+
output: process.stdout
|
|
121
|
+
});
|
|
122
|
+
const prompt = () => {
|
|
123
|
+
rl.question(chalk.bold("You > "), async (input) => {
|
|
124
|
+
const trimmed = input.trim();
|
|
125
|
+
if (!trimmed) {
|
|
126
|
+
prompt();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (trimmed.toLowerCase() === "exit" || trimmed.toLowerCase() === "quit") {
|
|
130
|
+
await endSessionCleanly(chat);
|
|
131
|
+
rl.close();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
if (stream) {
|
|
136
|
+
process.stdout.write(chalk.cyan("Agent > "));
|
|
137
|
+
const reply = await chat.send(trimmed, {
|
|
138
|
+
stream: true,
|
|
139
|
+
onChunk: (delta) => process.stdout.write(delta)
|
|
140
|
+
});
|
|
141
|
+
console.log();
|
|
142
|
+
if (reply.latencyMs) {
|
|
143
|
+
printInfo(`${reply.latencyMs}ms`);
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
const reply = await chat.send(trimmed);
|
|
147
|
+
if (reply.toolCalls?.length) {
|
|
148
|
+
console.log(chalk.dim(" Tools:"));
|
|
149
|
+
printToolCalls(reply.toolCalls);
|
|
150
|
+
}
|
|
151
|
+
console.log(chalk.cyan("Agent >"), reply.message);
|
|
152
|
+
}
|
|
153
|
+
printBlank();
|
|
154
|
+
} catch (error) {
|
|
155
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
156
|
+
printError("Send failed", errorMessage);
|
|
157
|
+
printBlank();
|
|
158
|
+
}
|
|
159
|
+
prompt();
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
const handleSigint = async () => {
|
|
163
|
+
printBlank();
|
|
164
|
+
await endSessionCleanly(chat);
|
|
165
|
+
rl.close();
|
|
166
|
+
process.exit(0);
|
|
167
|
+
};
|
|
168
|
+
rl.on("SIGINT", () => {
|
|
169
|
+
void handleSigint();
|
|
170
|
+
});
|
|
171
|
+
rl.on("close", () => {
|
|
172
|
+
});
|
|
173
|
+
prompt();
|
|
174
|
+
} catch (error) {
|
|
175
|
+
spinner.fail("Failed to connect");
|
|
176
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
177
|
+
printError("Error", errorMessage);
|
|
178
|
+
process.exitCode = 1;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function printToolCalls(toolCalls) {
|
|
182
|
+
for (const tc of toolCalls) {
|
|
183
|
+
const statusIcon = tc.status === "success" ? chalk.green("\u2713") : tc.status === "error" ? chalk.red("\u2717") : tc.status === "timeout" ? chalk.yellow("\u23F1") : chalk.dim("\u2026");
|
|
184
|
+
const duration = tc.durationMs ? chalk.dim(` (${tc.durationMs}ms)`) : "";
|
|
185
|
+
const params = Object.keys(tc.parameters).length ? chalk.dim(` ${JSON.stringify(tc.parameters)}`) : "";
|
|
186
|
+
console.log(` ${statusIcon} ${chalk.yellow(tc.name)}${params}${duration}`);
|
|
187
|
+
if (tc.status === "error" && tc.error) {
|
|
188
|
+
console.log(` ${chalk.red(tc.error.message)}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async function endSessionCleanly(chat) {
|
|
193
|
+
try {
|
|
194
|
+
await chat.end();
|
|
195
|
+
printSuccess("Session ended");
|
|
196
|
+
} catch {
|
|
197
|
+
printInfo("Session closed");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export {
|
|
201
|
+
createChatCommand
|
|
202
|
+
};
|
|
203
|
+
//# sourceMappingURL=chat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/commands/chat.ts"],"sourcesContent":["import { Command } from 'commander';\nimport ora from 'ora';\nimport chalk from 'chalk';\nimport { createInterface } from 'node:readline';\nimport type { ChanlSDK, ToolCall } from '@chanl-ai/sdk';\nimport { createSdk } from '../utils/sdk-factory.js';\nimport {\n printError,\n printSuccess,\n printInfo,\n printBlank,\n isJsonOutput,\n printJson,\n} from '../utils/output.js';\n\n/**\n * Create the chat command\n */\nexport function createChatCommand(): Command {\n const chat = new Command('chat')\n .description('Start an interactive chat session with an agent')\n .argument('<agent-id>', 'ID of the agent to chat with')\n .option('-m, --message <text>', 'Send a single message and exit')\n .option('-s, --stream', 'Enable streaming responses (real-time text)')\n .option('-w, --workspace <id>', 'Workspace ID override')\n .addHelpText(\n 'after',\n `\nWhat is Chat?\n Chat lets you test an AI agent via text in your terminal. It creates a session\n through interactions-service which manages the full lifecycle including\n transcription, billing, and evaluation.\n\nModes:\n Interactive (default): Opens a prompt loop — type messages, see agent responses.\n Type 'exit', 'quit', or press Ctrl+C to end.\n\n Single message: Send one message with --message, print response, exit.\n\nStreaming:\n Use --stream for real-time text output. Text appears as the agent generates it.\n Note: streaming responses do not include tool call metadata.\n\nExamples:\n $ chanl chat abc123 # Interactive chat\n $ chanl chat abc123 --stream # Interactive with streaming\n $ chanl chat abc123 --message \"Hello\" # Single message mode\n $ chanl chat abc123 --message \"Hi\" --stream # Single message, streamed\n $ chanl chat abc123 --message \"Hi\" --json # Single message, JSON output\n $ chanl chat abc123 --workspace ws_456 # Override workspace`\n )\n .action(handleChat);\n\n return chat;\n}\n\n/**\n * Handle the chat command\n */\nasync function handleChat(\n agentId: string,\n options: { message?: string; stream?: boolean; workspace?: string },\n): Promise<void> {\n const sdk = createSdk();\n if (!sdk) return;\n\n if (options.message) {\n await handleSingleMessage(sdk, agentId, options.message, options.stream);\n } else {\n await handleInteractiveChat(sdk, agentId, options.stream);\n }\n}\n\n/**\n * Single message mode: create session, send message, print response, end session\n * Uses SDK LiveChat for session management\n */\nasync function handleSingleMessage(\n sdk: ChanlSDK,\n agentId: string,\n message: string,\n stream?: boolean,\n): Promise<void> {\n const spinner = ora('Connecting to agent...').start();\n\n try {\n const chat = await sdk.chat.session(agentId);\n spinner.text = 'Sending message...';\n\n try {\n if (stream && !isJsonOutput()) {\n // Streaming mode: show text as it arrives\n spinner.stop();\n printBlank();\n console.log(chalk.dim('You >'), message);\n process.stdout.write(chalk.cyan('Agent > '));\n\n const reply = await chat.send(message, {\n stream: true,\n onChunk: (delta) => process.stdout.write(delta),\n });\n\n // Newline after streamed text\n console.log();\n if (reply.latencyMs) {\n printBlank();\n printInfo(`Response time: ${reply.latencyMs}ms`);\n }\n printBlank();\n } else {\n // Non-streaming or JSON mode\n const reply = await chat.send(message);\n spinner.stop();\n\n if (isJsonOutput()) {\n printJson({\n sessionId: chat.sessionId,\n agentId,\n message,\n response: reply.message,\n toolCalls: reply.toolCalls,\n latencyMs: reply.latencyMs,\n });\n } else {\n printBlank();\n console.log(chalk.dim('You >'), message);\n if (reply.toolCalls?.length) {\n console.log(chalk.dim(' Tools:'));\n printToolCalls(reply.toolCalls);\n }\n console.log(chalk.cyan('Agent >'), reply.message);\n if (reply.latencyMs) {\n printBlank();\n printInfo(`Response time: ${reply.latencyMs}ms`);\n }\n printBlank();\n }\n }\n } finally {\n await chat.end();\n }\n } catch (error) {\n spinner.fail('Chat failed');\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', errorMessage);\n process.exitCode = 1;\n }\n}\n\n/**\n * Interactive mode: readline prompt loop with agent\n * Uses SDK LiveChat for session management\n */\nasync function handleInteractiveChat(\n sdk: ChanlSDK,\n agentId: string,\n stream?: boolean,\n): Promise<void> {\n const spinner = ora('Connecting to agent...').start();\n\n try {\n const chat = await sdk.chat.session(agentId);\n const agentName = chat.agentName || 'Agent';\n spinner.succeed(`Connected to ${agentName}${stream ? ' (streaming)' : ''}`);\n printInfo(\"Type 'exit' or 'quit' to end the session. Ctrl+C also works.\");\n printBlank();\n\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const prompt = (): void => {\n rl.question(chalk.bold('You > '), async (input) => {\n const trimmed = input.trim();\n\n if (!trimmed) {\n prompt();\n return;\n }\n\n if (trimmed.toLowerCase() === 'exit' || trimmed.toLowerCase() === 'quit') {\n await endSessionCleanly(chat);\n rl.close();\n return;\n }\n\n try {\n if (stream) {\n // Streaming: show text character-by-character\n process.stdout.write(chalk.cyan('Agent > '));\n const reply = await chat.send(trimmed, {\n stream: true,\n onChunk: (delta) => process.stdout.write(delta),\n });\n console.log(); // newline after stream\n if (reply.latencyMs) {\n printInfo(`${reply.latencyMs}ms`);\n }\n } else {\n const reply = await chat.send(trimmed);\n if (reply.toolCalls?.length) {\n console.log(chalk.dim(' Tools:'));\n printToolCalls(reply.toolCalls);\n }\n console.log(chalk.cyan('Agent >'), reply.message);\n }\n printBlank();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n printError('Send failed', errorMessage);\n printBlank();\n }\n\n prompt();\n });\n };\n\n // Handle Ctrl+C gracefully\n const handleSigint = async (): Promise<void> => {\n printBlank();\n await endSessionCleanly(chat);\n rl.close();\n process.exit(0);\n };\n\n rl.on('SIGINT', () => {\n void handleSigint();\n });\n\n rl.on('close', () => {\n // Readline closed (e.g., piped input ended)\n });\n\n prompt();\n } catch (error) {\n spinner.fail('Failed to connect');\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', errorMessage);\n process.exitCode = 1;\n }\n}\n\n/**\n * Display tool calls in a readable format\n */\nfunction printToolCalls(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 * End a chat session with user feedback\n */\nasync function endSessionCleanly(\n chat: { end: () => Promise<void> },\n): Promise<void> {\n try {\n await chat.end();\n printSuccess('Session ended');\n } catch {\n printInfo('Session closed');\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { configStore } from "../utils/config-store.js";
|
|
4
|
+
import {
|
|
5
|
+
printSuccess,
|
|
6
|
+
printError,
|
|
7
|
+
printLabel,
|
|
8
|
+
printBlank,
|
|
9
|
+
printTable,
|
|
10
|
+
isJsonOutput,
|
|
11
|
+
printJson,
|
|
12
|
+
maskString
|
|
13
|
+
} from "../utils/output.js";
|
|
14
|
+
const VALID_KEYS = [
|
|
15
|
+
"apiKey",
|
|
16
|
+
"baseUrl",
|
|
17
|
+
"workspaceId",
|
|
18
|
+
"defaultFormat"
|
|
19
|
+
];
|
|
20
|
+
const ENVIRONMENT_PRESETS = {
|
|
21
|
+
production: "https://platform.chanl.ai"
|
|
22
|
+
};
|
|
23
|
+
const KEY_DESCRIPTIONS = {
|
|
24
|
+
apiKey: "Chanl API key for authentication",
|
|
25
|
+
jwtToken: "JWT access token (from browser login)",
|
|
26
|
+
refreshToken: "JWT refresh token (from browser login)",
|
|
27
|
+
baseUrl: "Base URL for the Chanl API",
|
|
28
|
+
workspaceId: "Default workspace ID",
|
|
29
|
+
appUrl: "App URL for browser-based flows",
|
|
30
|
+
defaultFormat: "Default output format (table or json)"
|
|
31
|
+
};
|
|
32
|
+
function createConfigCommand() {
|
|
33
|
+
const config = new Command("config").description("Manage CLI configuration");
|
|
34
|
+
config.command("list").description("List all configuration values").action(handleConfigList);
|
|
35
|
+
config.command("get <key>").description("Get a configuration value").action(handleConfigGet);
|
|
36
|
+
config.command("set <key> <value>").description("Set a configuration value").action(handleConfigSet);
|
|
37
|
+
config.command("delete <key>").alias("unset").description("Delete a configuration value").action(handleConfigDelete);
|
|
38
|
+
config.command("path").description("Show path to configuration file").action(handleConfigPath);
|
|
39
|
+
config.command("use <environment>").description("Reset to production API (or set a custom base URL with `config set baseUrl`)").action(handleConfigUse);
|
|
40
|
+
return config;
|
|
41
|
+
}
|
|
42
|
+
function handleConfigList() {
|
|
43
|
+
const allConfig = configStore.getAll();
|
|
44
|
+
if (isJsonOutput()) {
|
|
45
|
+
const safeConfig = {
|
|
46
|
+
...allConfig,
|
|
47
|
+
apiKey: allConfig.apiKey ? maskString(allConfig.apiKey) : void 0
|
|
48
|
+
};
|
|
49
|
+
printJson(safeConfig);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
printBlank();
|
|
53
|
+
const rows = VALID_KEYS.map((key) => {
|
|
54
|
+
let value = allConfig[key];
|
|
55
|
+
const source = getConfigSource(key);
|
|
56
|
+
if (key === "apiKey" && typeof value === "string") {
|
|
57
|
+
value = maskString(value);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
key,
|
|
61
|
+
value: value ?? chalk.dim("(not set)"),
|
|
62
|
+
source,
|
|
63
|
+
description: KEY_DESCRIPTIONS[key]
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
printTable(
|
|
67
|
+
[
|
|
68
|
+
{ header: "Key", key: "key", width: 15 },
|
|
69
|
+
{ header: "Value", key: "value", width: 30 },
|
|
70
|
+
{ header: "Source", key: "source", width: 10 }
|
|
71
|
+
],
|
|
72
|
+
rows
|
|
73
|
+
);
|
|
74
|
+
printBlank();
|
|
75
|
+
printLabel("Config file", configStore.getPath());
|
|
76
|
+
printBlank();
|
|
77
|
+
}
|
|
78
|
+
function handleConfigGet(key) {
|
|
79
|
+
if (!isValidKey(key)) {
|
|
80
|
+
printError(`Unknown configuration key: ${key}`);
|
|
81
|
+
printBlank();
|
|
82
|
+
printLabel("Valid keys", VALID_KEYS.join(", "));
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const typedKey = key;
|
|
87
|
+
let value;
|
|
88
|
+
switch (typedKey) {
|
|
89
|
+
case "apiKey":
|
|
90
|
+
value = configStore.getApiKey();
|
|
91
|
+
break;
|
|
92
|
+
case "baseUrl":
|
|
93
|
+
value = configStore.getBaseUrl();
|
|
94
|
+
break;
|
|
95
|
+
case "workspaceId":
|
|
96
|
+
value = configStore.getWorkspaceId();
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
value = configStore.get(typedKey);
|
|
100
|
+
}
|
|
101
|
+
if (isJsonOutput()) {
|
|
102
|
+
printJson({
|
|
103
|
+
key,
|
|
104
|
+
value: key === "apiKey" && value ? maskString(value) : value,
|
|
105
|
+
source: getConfigSource(typedKey)
|
|
106
|
+
});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (value === void 0) {
|
|
110
|
+
console.log(chalk.dim("(not set)"));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (key === "apiKey") {
|
|
114
|
+
console.log(maskString(value));
|
|
115
|
+
} else {
|
|
116
|
+
console.log(value);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function handleConfigSet(key, value) {
|
|
120
|
+
if (!isValidKey(key)) {
|
|
121
|
+
printError(`Unknown configuration key: ${key}`);
|
|
122
|
+
printBlank();
|
|
123
|
+
printLabel("Valid keys", VALID_KEYS.join(", "));
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const typedKey = key;
|
|
128
|
+
if (typedKey === "defaultFormat") {
|
|
129
|
+
if (value !== "table" && value !== "json") {
|
|
130
|
+
printError(`Invalid format: ${value}`);
|
|
131
|
+
printLabel("Valid values", "table, json");
|
|
132
|
+
process.exitCode = 1;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (typedKey === "baseUrl") {
|
|
137
|
+
try {
|
|
138
|
+
new URL(value);
|
|
139
|
+
} catch {
|
|
140
|
+
printError(`Invalid URL: ${value}`);
|
|
141
|
+
process.exitCode = 1;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
configStore.set(typedKey, value);
|
|
146
|
+
if (isJsonOutput()) {
|
|
147
|
+
printJson({
|
|
148
|
+
success: true,
|
|
149
|
+
key,
|
|
150
|
+
value: key === "apiKey" ? maskString(value) : value
|
|
151
|
+
});
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const displayValue = key === "apiKey" ? maskString(value) : value;
|
|
155
|
+
printSuccess(`Set ${key} = ${displayValue}`);
|
|
156
|
+
}
|
|
157
|
+
function handleConfigDelete(key) {
|
|
158
|
+
if (!isValidKey(key)) {
|
|
159
|
+
printError(`Unknown configuration key: ${key}`);
|
|
160
|
+
printBlank();
|
|
161
|
+
printLabel("Valid keys", VALID_KEYS.join(", "));
|
|
162
|
+
process.exitCode = 1;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const typedKey = key;
|
|
166
|
+
if (!configStore.has(typedKey)) {
|
|
167
|
+
if (isJsonOutput()) {
|
|
168
|
+
printJson({ success: true, key, message: "Key was not set" });
|
|
169
|
+
} else {
|
|
170
|
+
printSuccess(`${key} was not set`);
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
configStore.delete(typedKey);
|
|
175
|
+
if (isJsonOutput()) {
|
|
176
|
+
printJson({ success: true, key, message: "Deleted" });
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
printSuccess(`Deleted ${key}`);
|
|
180
|
+
}
|
|
181
|
+
function handleConfigPath() {
|
|
182
|
+
const path = configStore.getPath();
|
|
183
|
+
if (isJsonOutput()) {
|
|
184
|
+
printJson({ path });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
console.log(path);
|
|
188
|
+
}
|
|
189
|
+
function isValidKey(key) {
|
|
190
|
+
return VALID_KEYS.includes(key);
|
|
191
|
+
}
|
|
192
|
+
function getConfigSource(key) {
|
|
193
|
+
const envVarMap = {
|
|
194
|
+
apiKey: "CHANL_API_KEY",
|
|
195
|
+
baseUrl: "CHANL_BASE_URL",
|
|
196
|
+
workspaceId: "CHANL_WORKSPACE_ID"
|
|
197
|
+
};
|
|
198
|
+
const envVar = envVarMap[key];
|
|
199
|
+
if (envVar && process.env[envVar]) {
|
|
200
|
+
return chalk.cyan("env");
|
|
201
|
+
}
|
|
202
|
+
if (configStore.has(key)) {
|
|
203
|
+
return chalk.green("config");
|
|
204
|
+
}
|
|
205
|
+
return chalk.dim("default");
|
|
206
|
+
}
|
|
207
|
+
function handleConfigUse(environment) {
|
|
208
|
+
const env = environment.toLowerCase();
|
|
209
|
+
const url = ENVIRONMENT_PRESETS[env];
|
|
210
|
+
if (!url) {
|
|
211
|
+
printError(`Unknown environment: ${environment}`);
|
|
212
|
+
printBlank();
|
|
213
|
+
printLabel("Available environments", Object.keys(ENVIRONMENT_PRESETS).join(", "));
|
|
214
|
+
process.exitCode = 1;
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
configStore.set("baseUrl", url);
|
|
218
|
+
if (isJsonOutput()) {
|
|
219
|
+
printJson({
|
|
220
|
+
success: true,
|
|
221
|
+
environment: env,
|
|
222
|
+
baseUrl: url
|
|
223
|
+
});
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
printSuccess(`Switched to ${env}: ${url}`);
|
|
227
|
+
}
|
|
228
|
+
export {
|
|
229
|
+
createConfigCommand
|
|
230
|
+
};
|
|
231
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/commands/config.ts"],"sourcesContent":["import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { configStore, type ChanlCliConfig } from '../utils/config-store.js';\nimport {\n printSuccess,\n printError,\n printLabel,\n printBlank,\n printTable,\n isJsonOutput,\n printJson,\n maskString,\n} from '../utils/output.js';\n\n/**\n * Valid configuration keys\n */\nconst VALID_KEYS: (keyof ChanlCliConfig)[] = [\n 'apiKey',\n 'baseUrl',\n 'workspaceId',\n 'defaultFormat',\n];\n\n/**\n * Environment presets for quick switching\n * Only production is exposed publicly. Use `chanl config set baseUrl <url>` for custom endpoints.\n */\nconst ENVIRONMENT_PRESETS: Record<string, string> = {\n production: 'https://platform.chanl.ai',\n};\n\n/**\n * Configuration key descriptions\n */\nconst KEY_DESCRIPTIONS: Record<keyof ChanlCliConfig, string> = {\n apiKey: 'Chanl API key for authentication',\n jwtToken: 'JWT access token (from browser login)',\n refreshToken: 'JWT refresh token (from browser login)',\n baseUrl: 'Base URL for the Chanl API',\n workspaceId: 'Default workspace ID',\n appUrl: 'App URL for browser-based flows',\n defaultFormat: 'Default output format (table or json)',\n};\n\n/**\n * Create the config command group\n */\nexport function createConfigCommand(): Command {\n const config = new Command('config').description('Manage CLI configuration');\n\n // `chanl config list` subcommand\n config\n .command('list')\n .description('List all configuration values')\n .action(handleConfigList);\n\n // `chanl config get <key>` subcommand\n config\n .command('get <key>')\n .description('Get a configuration value')\n .action(handleConfigGet);\n\n // `chanl config set <key> <value>` subcommand\n config\n .command('set <key> <value>')\n .description('Set a configuration value')\n .action(handleConfigSet);\n\n // `chanl config delete <key>` subcommand\n config\n .command('delete <key>')\n .alias('unset')\n .description('Delete a configuration value')\n .action(handleConfigDelete);\n\n // `chanl config path` subcommand\n config\n .command('path')\n .description('Show path to configuration file')\n .action(handleConfigPath);\n\n // `chanl config use <environment>` subcommand\n config\n .command('use <environment>')\n .description('Reset to production API (or set a custom base URL with `config set baseUrl`)')\n .action(handleConfigUse);\n\n return config;\n}\n\n/**\n * Handle config list command\n */\nfunction handleConfigList(): void {\n const allConfig = configStore.getAll();\n\n if (isJsonOutput()) {\n // Mask sensitive values in JSON output\n const safeConfig = {\n ...allConfig,\n apiKey: allConfig.apiKey ? maskString(allConfig.apiKey) : undefined,\n };\n printJson(safeConfig);\n return;\n }\n\n printBlank();\n\n const rows = VALID_KEYS.map((key) => {\n let value = allConfig[key];\n const source = getConfigSource(key);\n\n // Mask API key\n if (key === 'apiKey' && typeof value === 'string') {\n value = maskString(value);\n }\n\n return {\n key,\n value: value ?? chalk.dim('(not set)'),\n source,\n description: KEY_DESCRIPTIONS[key],\n };\n });\n\n printTable(\n [\n { header: 'Key', key: 'key', width: 15 },\n { header: 'Value', key: 'value', width: 30 },\n { header: 'Source', key: 'source', width: 10 },\n ],\n rows\n );\n\n printBlank();\n printLabel('Config file', configStore.getPath());\n printBlank();\n}\n\n/**\n * Handle config get command\n */\nfunction handleConfigGet(key: string): void {\n if (!isValidKey(key)) {\n printError(`Unknown configuration key: ${key}`);\n printBlank();\n printLabel('Valid keys', VALID_KEYS.join(', '));\n process.exitCode = 1;\n return;\n }\n\n const typedKey = key as keyof ChanlCliConfig;\n let value: string | undefined;\n\n // Get value with env var fallback\n switch (typedKey) {\n case 'apiKey':\n value = configStore.getApiKey();\n break;\n case 'baseUrl':\n value = configStore.getBaseUrl();\n break;\n case 'workspaceId':\n value = configStore.getWorkspaceId();\n break;\n default:\n value = configStore.get(typedKey) as string | undefined;\n }\n\n if (isJsonOutput()) {\n printJson({\n key,\n value: key === 'apiKey' && value ? maskString(value) : value,\n source: getConfigSource(typedKey),\n });\n return;\n }\n\n if (value === undefined) {\n console.log(chalk.dim('(not set)'));\n return;\n }\n\n // Mask API key even in regular output\n if (key === 'apiKey') {\n console.log(maskString(value));\n } else {\n console.log(value);\n }\n}\n\n/**\n * Handle config set command\n */\nfunction handleConfigSet(key: string, value: string): void {\n if (!isValidKey(key)) {\n printError(`Unknown configuration key: ${key}`);\n printBlank();\n printLabel('Valid keys', VALID_KEYS.join(', '));\n process.exitCode = 1;\n return;\n }\n\n const typedKey = key as keyof ChanlCliConfig;\n\n // Validate specific keys\n if (typedKey === 'defaultFormat') {\n if (value !== 'table' && value !== 'json') {\n printError(`Invalid format: ${value}`);\n printLabel('Valid values', 'table, json');\n process.exitCode = 1;\n return;\n }\n }\n\n if (typedKey === 'baseUrl') {\n try {\n new URL(value);\n } catch {\n printError(`Invalid URL: ${value}`);\n process.exitCode = 1;\n return;\n }\n }\n\n // Set the value\n configStore.set(typedKey, value as never);\n\n if (isJsonOutput()) {\n printJson({\n success: true,\n key,\n value: key === 'apiKey' ? maskString(value) : value,\n });\n return;\n }\n\n const displayValue = key === 'apiKey' ? maskString(value) : value;\n printSuccess(`Set ${key} = ${displayValue}`);\n}\n\n/**\n * Handle config delete command\n */\nfunction handleConfigDelete(key: string): void {\n if (!isValidKey(key)) {\n printError(`Unknown configuration key: ${key}`);\n printBlank();\n printLabel('Valid keys', VALID_KEYS.join(', '));\n process.exitCode = 1;\n return;\n }\n\n const typedKey = key as keyof ChanlCliConfig;\n\n // Check if key exists\n if (!configStore.has(typedKey)) {\n if (isJsonOutput()) {\n printJson({ success: true, key, message: 'Key was not set' });\n } else {\n printSuccess(`${key} was not set`);\n }\n return;\n }\n\n configStore.delete(typedKey);\n\n if (isJsonOutput()) {\n printJson({ success: true, key, message: 'Deleted' });\n return;\n }\n\n printSuccess(`Deleted ${key}`);\n}\n\n/**\n * Handle config path command\n */\nfunction handleConfigPath(): void {\n const path = configStore.getPath();\n\n if (isJsonOutput()) {\n printJson({ path });\n return;\n }\n\n console.log(path);\n}\n\n/**\n * Check if a key is a valid configuration key\n */\nfunction isValidKey(key: string): key is keyof ChanlCliConfig {\n return VALID_KEYS.includes(key as keyof ChanlCliConfig);\n}\n\n/**\n * Get the source of a configuration value\n */\nfunction getConfigSource(key: keyof ChanlCliConfig): string {\n const envVarMap: Partial<Record<keyof ChanlCliConfig, string>> = {\n apiKey: 'CHANL_API_KEY',\n baseUrl: 'CHANL_BASE_URL',\n workspaceId: 'CHANL_WORKSPACE_ID',\n };\n\n const envVar = envVarMap[key];\n if (envVar && process.env[envVar]) {\n return chalk.cyan('env');\n }\n\n if (configStore.has(key)) {\n return chalk.green('config');\n }\n\n return chalk.dim('default');\n}\n\n/**\n * Handle config use command - switch to a preset environment\n */\nfunction handleConfigUse(environment: string): void {\n const env = environment.toLowerCase();\n const url = ENVIRONMENT_PRESETS[env];\n\n if (!url) {\n printError(`Unknown environment: ${environment}`);\n printBlank();\n printLabel('Available environments', Object.keys(ENVIRONMENT_PRESETS).join(', '));\n process.exitCode = 1;\n return;\n }\n\n configStore.set('baseUrl', url);\n\n if (isJsonOutput()) {\n printJson({\n success: true,\n environment: env,\n baseUrl: url,\n });\n return;\n }\n\n printSuccess(`Switched to ${env}: ${url}`);\n}\n"],"mappings":"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","names":[]}
|