@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.
Files changed (77) hide show
  1. package/bin/chanl.js +10 -0
  2. package/dist/__tests__/cli.test.d.ts +2 -0
  3. package/dist/__tests__/cli.test.js +2313 -0
  4. package/dist/__tests__/cli.test.js.map +1 -0
  5. package/dist/cli.d.ts +12 -0
  6. package/dist/cli.js +72 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/agents.d.ts +8 -0
  9. package/dist/commands/agents.js +671 -0
  10. package/dist/commands/agents.js.map +1 -0
  11. package/dist/commands/auth.d.ts +16 -0
  12. package/dist/commands/auth.js +294 -0
  13. package/dist/commands/auth.js.map +1 -0
  14. package/dist/commands/call.d.ts +8 -0
  15. package/dist/commands/call.js +166 -0
  16. package/dist/commands/call.js.map +1 -0
  17. package/dist/commands/calls.d.ts +8 -0
  18. package/dist/commands/calls.js +719 -0
  19. package/dist/commands/calls.js.map +1 -0
  20. package/dist/commands/chat.d.ts +8 -0
  21. package/dist/commands/chat.js +203 -0
  22. package/dist/commands/chat.js.map +1 -0
  23. package/dist/commands/config.d.ts +8 -0
  24. package/dist/commands/config.js +231 -0
  25. package/dist/commands/config.js.map +1 -0
  26. package/dist/commands/health.d.ts +8 -0
  27. package/dist/commands/health.js +55 -0
  28. package/dist/commands/health.js.map +1 -0
  29. package/dist/commands/index.d.ts +18 -0
  30. package/dist/commands/index.js +39 -0
  31. package/dist/commands/index.js.map +1 -0
  32. package/dist/commands/knowledge.d.ts +8 -0
  33. package/dist/commands/knowledge.js +539 -0
  34. package/dist/commands/knowledge.js.map +1 -0
  35. package/dist/commands/mcp.d.ts +8 -0
  36. package/dist/commands/mcp.js +589 -0
  37. package/dist/commands/mcp.js.map +1 -0
  38. package/dist/commands/memory.d.ts +8 -0
  39. package/dist/commands/memory.js +408 -0
  40. package/dist/commands/memory.js.map +1 -0
  41. package/dist/commands/personas.d.ts +8 -0
  42. package/dist/commands/personas.js +356 -0
  43. package/dist/commands/personas.js.map +1 -0
  44. package/dist/commands/prompts.d.ts +8 -0
  45. package/dist/commands/prompts.js +295 -0
  46. package/dist/commands/prompts.js.map +1 -0
  47. package/dist/commands/scenarios.d.ts +8 -0
  48. package/dist/commands/scenarios.js +591 -0
  49. package/dist/commands/scenarios.js.map +1 -0
  50. package/dist/commands/scorecards.d.ts +8 -0
  51. package/dist/commands/scorecards.js +570 -0
  52. package/dist/commands/scorecards.js.map +1 -0
  53. package/dist/commands/tools.d.ts +8 -0
  54. package/dist/commands/tools.js +632 -0
  55. package/dist/commands/tools.js.map +1 -0
  56. package/dist/commands/toolsets.d.ts +8 -0
  57. package/dist/commands/toolsets.js +464 -0
  58. package/dist/commands/toolsets.js.map +1 -0
  59. package/dist/commands/workspaces.d.ts +8 -0
  60. package/dist/commands/workspaces.js +170 -0
  61. package/dist/commands/workspaces.js.map +1 -0
  62. package/dist/index.d.ts +2 -0
  63. package/dist/index.js +6 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/utils/config-store.d.ts +117 -0
  66. package/dist/utils/config-store.js +191 -0
  67. package/dist/utils/config-store.js.map +1 -0
  68. package/dist/utils/interactive.d.ts +41 -0
  69. package/dist/utils/interactive.js +83 -0
  70. package/dist/utils/interactive.js.map +1 -0
  71. package/dist/utils/output.d.ts +100 -0
  72. package/dist/utils/output.js +221 -0
  73. package/dist/utils/output.js.map +1 -0
  74. package/dist/utils/sdk-factory.d.ts +15 -0
  75. package/dist/utils/sdk-factory.js +34 -0
  76. package/dist/utils/sdk-factory.js.map +1 -0
  77. package/package.json +67 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/commands/knowledge.ts"],"sourcesContent":["import { Command } from 'commander';\nimport ora from 'ora';\nimport chalk from 'chalk';\nimport { readFileSync, existsSync } from 'fs';\nimport { resolve } from 'path';\nimport { ChanlSDK } from '@chanl-ai/sdk';\nimport type {\n Knowledge,\n CreateKnowledgeData,\n KnowledgeFilters,\n SearchKnowledgeData,\n} from '@chanl-ai/sdk';\nimport { configStore } from '../utils/config-store.js';\nimport {\n printError,\n printSuccess,\n printInfo,\n printBlank,\n printSimpleTable,\n printLabel,\n isJsonOutput,\n printJson,\n formatDate,\n} from '../utils/output.js';\n\n/**\n * Create the knowledge command group\n */\nexport function createKnowledgeCommand(): Command {\n const knowledge = new Command('kb')\n .alias('knowledge')\n .description('Manage knowledge base entries (RAG)')\n .addHelpText(\n 'after',\n `\nWhat is Knowledge Base?\n The knowledge base stores documents, FAQs, and other content that AI agents\n can search and retrieve during conversations. Supports text, URLs, and files.\n\nQuick Start:\n $ chanl kb list # List all entries\n $ chanl kb add --text \"FAQ content\" --title \"FAQ\" # Add text content\n $ chanl kb add --url https://docs.example.com # Add from URL\n $ chanl kb add --file ./docs/guide.pdf # Upload file\n $ chanl kb search \"how do I reset password\" # Search KB\n\nWorkflow:\n 1. Add content via text, URL, or file upload\n 2. Search to verify content is indexed\n 3. Associate with agents for RAG retrieval`\n );\n\n // kb list\n knowledge\n .command('list')\n .description('List knowledge base entries')\n .option('-s, --search <query>', 'Filter by title/content search')\n .option('--source <source>', 'Filter by source (text, url, file, manual)')\n .option('--status <status>', 'Filter by processing status')\n .option('--folder <folderId>', 'Filter by folder ID')\n .option('-l, --limit <number>', 'Number of items per page', '20')\n .option('--page <number>', 'Page number', '1')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl kb list # List all entries\n $ chanl kb list --source url # Only URL-sourced entries\n $ chanl kb list --status completed # Only fully processed\n $ chanl kb list --json # Output as JSON`\n )\n .action(handleKnowledgeList);\n\n // kb get <id>\n knowledge\n .command('get <id>')\n .description('Get knowledge entry details')\n .option('--chunks', 'Include chunk details')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl kb get abc123 # Get entry details\n $ chanl kb get abc123 --chunks # Include chunks\n $ chanl kb get abc123 --json # Output as JSON`\n )\n .action(handleKnowledgeGet);\n\n // kb add (create)\n knowledge\n .command('add')\n .description('Add knowledge content (text, URL, or file)')\n .option('-t, --title <title>', 'Title for the entry')\n .option('--text <content>', 'Direct text content')\n .option('--url <url>', 'URL to fetch content from')\n .option('-f, --file <path>', 'File to upload (PDF, TXT, HTML, MD)')\n .option('--folder <folderId>', 'Folder ID to organize entry')\n .option('--tags <tags>', 'Comma-separated tags')\n .option('--category <category>', 'Category for organization')\n .option('--crawl-depth <depth>', 'URL crawl depth (0-2)', '0')\n .option('--max-pages <pages>', 'Max pages to crawl (1-50)', '10')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl kb add --text \"Content here\" --title \"FAQ\"\n $ chanl kb add --url https://docs.example.com --title \"Docs\"\n $ chanl kb add --url https://docs.example.com --crawl-depth 1 --max-pages 20\n $ chanl kb add --file ./guide.pdf --title \"User Guide\"\n $ chanl kb add --text \"Content\" --tags \"faq,support\" --category \"support\"`\n )\n .action(handleKnowledgeAdd);\n\n // kb update <id>\n knowledge\n .command('update <id>')\n .description('Update a knowledge entry')\n .option('-t, --title <title>', 'New title')\n .option('--content <content>', 'New content (text entries only)')\n .option('--tags <tags>', 'Comma-separated tags')\n .option('--category <category>', 'Category')\n .option('--enabled <boolean>', 'Enable/disable for search')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl kb update abc123 --title \"New Title\"\n $ chanl kb update abc123 --enabled false\n $ chanl kb update abc123 --tags \"updated,important\"`\n )\n .action(handleKnowledgeUpdate);\n\n // kb delete <id>\n knowledge\n .command('delete <id>')\n .description('Delete a knowledge entry')\n .option('-y, --yes', 'Skip confirmation prompt')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl kb delete abc123\n $ chanl kb delete abc123 --yes # Skip confirmation`\n )\n .action(handleKnowledgeDelete);\n\n // kb search <query>\n knowledge\n .command('search <query>')\n .description('Search the knowledge base')\n .option('-m, --mode <mode>', 'Search mode: hybrid, vector, text', 'hybrid')\n .option('-l, --limit <number>', 'Max results', '10')\n .option('--min-score <score>', 'Minimum similarity score (0-1)')\n .addHelpText(\n 'after',\n `\nSearch Modes:\n hybrid - Combined vector + text search (default, best results)\n vector - Semantic similarity search\n text - Full-text keyword search\n\nExamples:\n $ chanl kb search \"how to reset password\"\n $ chanl kb search \"API authentication\" --mode vector\n $ chanl kb search \"billing\" --limit 5 --min-score 0.7`\n )\n .action(handleKnowledgeSearch);\n\n // kb status <taskId>\n knowledge\n .command('status <taskId>')\n .description('Check processing status of async upload')\n .addHelpText(\n 'after',\n `\nExamples:\n $ chanl kb status abc123 # Check task status`\n )\n .action(handleKnowledgeStatus);\n\n return knowledge;\n}\n\n// ============================================================================\n// HANDLERS\n// ============================================================================\n\nasync function handleKnowledgeList(options: {\n search?: string;\n source?: string;\n status?: string;\n folder?: string;\n limit: string;\n page: string;\n}): Promise<void> {\n const apiKey = configStore.getApiKey();\n if (!apiKey) {\n printError('Not logged in', 'Run: chanl auth login');\n process.exitCode = 1;\n return;\n }\n\n const sdk = new ChanlSDK({\n baseUrl: configStore.getBaseUrl(),\n apiKey,\n });\n\n const spinner = ora('Fetching knowledge entries...').start();\n\n try {\n const filters: KnowledgeFilters = {\n search: options.search,\n source: options.source as any,\n processingStatus: options.status as any,\n folderId: options.folder,\n limit: parseInt(options.limit),\n page: parseInt(options.page),\n };\n\n const response = await sdk.knowledge.list(filters);\n spinner.stop();\n\n if (!response.success) {\n printError('Failed to list knowledge entries', response.message);\n process.exitCode = 1;\n return;\n }\n\n // API returns { items: [...], pagination: {...}, total: number }\n const responseData = response.data as { items?: Knowledge[]; total?: number };\n const entries = responseData?.items || [];\n const total = responseData?.total ?? entries.length;\n\n if (isJsonOutput()) {\n printJson(response.data);\n return;\n }\n\n if (entries.length === 0) {\n printInfo('No knowledge entries found');\n return;\n }\n\n printBlank();\n console.log(chalk.bold(`Knowledge Base (${total} entries)`));\n printBlank();\n\n printSimpleTable(\n ['ID', 'Title', 'Source', 'Status', 'Chunks', 'Created'],\n entries.map((entry: Knowledge) => [\n entry.id.slice(0, 8),\n (entry.title || 'Untitled').slice(0, 40),\n entry.source,\n formatStatus(entry.processingStatus),\n String(entry.chunkCount || '-'),\n formatDate(entry.createdAt),\n ])\n );\n printBlank();\n } catch (error) {\n spinner.fail('Failed to list knowledge entries');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\nasync function handleKnowledgeGet(\n id: string,\n options: { chunks?: boolean }\n): Promise<void> {\n const apiKey = configStore.getApiKey();\n if (!apiKey) {\n printError('Not logged in', 'Run: chanl auth login');\n process.exitCode = 1;\n return;\n }\n\n const sdk = new ChanlSDK({\n baseUrl: configStore.getBaseUrl(),\n apiKey,\n });\n\n const spinner = ora('Fetching knowledge entry...').start();\n\n try {\n const response = await sdk.knowledge.get(id);\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Knowledge entry not found', response.message);\n process.exitCode = 1;\n return;\n }\n\n // API returns { knowledge: {...} } wrapper\n const responseData = response.data as { knowledge?: Knowledge } | Knowledge;\n const entry = 'knowledge' in responseData && responseData.knowledge ? responseData.knowledge : responseData as Knowledge;\n\n if (isJsonOutput()) {\n printJson(entry);\n return;\n }\n\n printBlank();\n console.log(chalk.bold('Knowledge Entry Details'));\n printBlank();\n\n printLabel('ID', entry.id);\n printLabel('Title', entry.title);\n printLabel('Source', entry.source);\n printLabel('Status', formatStatus(entry.processingStatus));\n if (entry.chunkCount) printLabel('Chunks', entry.chunkCount.toString());\n if (entry.tokenCount) printLabel('Tokens', entry.tokenCount.toString());\n if (entry.folderId) printLabel('Folder', entry.folderId);\n if (entry.metadata?.category) printLabel('Category', entry.metadata.category);\n if (entry.metadata?.tags?.length) {\n printLabel('Tags', entry.metadata.tags.join(', '));\n }\n if (entry.createdAt) printLabel('Created', formatDate(entry.createdAt));\n if (entry.updatedAt) printLabel('Updated', formatDate(entry.updatedAt));\n\n if (entry.content) {\n printBlank();\n console.log(chalk.bold('Content Preview:'));\n console.log(chalk.dim(entry.content.slice(0, 500) + (entry.content.length > 500 ? '...' : '')));\n }\n\n // Fetch chunks if requested\n if (options.chunks) {\n const chunksResponse = await sdk.knowledge.getChunks(id);\n if (chunksResponse.success && chunksResponse.data) {\n printBlank();\n console.log(chalk.bold(`Chunks (${chunksResponse.data.length}):`));\n chunksResponse.data.slice(0, 5).forEach((chunk, i) => {\n console.log(chalk.dim(` [${i + 1}] ${chunk.content.slice(0, 100)}...`));\n });\n if (chunksResponse.data.length > 5) {\n console.log(chalk.dim(` ... and ${chunksResponse.data.length - 5} more`));\n }\n }\n }\n\n printBlank();\n } catch (error) {\n spinner.fail('Failed to get knowledge entry');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\nasync function handleKnowledgeAdd(options: {\n title?: string;\n text?: string;\n url?: string;\n file?: string;\n folder?: string;\n tags?: string;\n category?: string;\n crawlDepth?: string;\n maxPages?: string;\n}): Promise<void> {\n const apiKey = configStore.getApiKey();\n if (!apiKey) {\n printError('Not logged in', 'Run: chanl auth login');\n process.exitCode = 1;\n return;\n }\n\n // Validate input - must have exactly one of text, url, or file\n const sources = [options.text, options.url, options.file].filter(Boolean);\n if (sources.length === 0) {\n printError('Missing content', 'Provide --text, --url, or --file');\n process.exitCode = 1;\n return;\n }\n if (sources.length > 1) {\n printError('Too many sources', 'Provide only one of --text, --url, or --file');\n process.exitCode = 1;\n return;\n }\n\n const sdk = new ChanlSDK({\n baseUrl: configStore.getBaseUrl(),\n apiKey,\n });\n\n const spinner = ora('Adding knowledge entry...').start();\n\n try {\n let response;\n\n if (options.file) {\n // File upload\n const filePath = resolve(options.file);\n if (!existsSync(filePath)) {\n spinner.fail('File not found');\n printError('File not found', filePath);\n process.exitCode = 1;\n return;\n }\n\n const fileBuffer = readFileSync(filePath);\n const filename = options.file.split('/').pop() || 'document';\n\n response = await sdk.knowledge.createFromFile(fileBuffer, filename, {\n title: options.title || filename,\n folderId: options.folder,\n metadata: {\n category: options.category,\n tags: options.tags?.split(',').map((t) => t.trim()),\n },\n });\n } else {\n // Text or URL\n const createData: CreateKnowledgeData = {\n title: options.title || (options.url ? 'URL Import' : 'Text Entry'),\n source: options.url ? 'url' : 'text',\n content: options.text,\n url: options.url,\n folderId: options.folder,\n metadata: {\n category: options.category,\n tags: options.tags?.split(',').map((t) => t.trim()),\n },\n };\n\n // Add crawl options for URL sources\n if (options.url) {\n createData.crawlOptions = {\n depth: parseInt(options.crawlDepth || '0'),\n maxPages: parseInt(options.maxPages || '10'),\n sameDomainOnly: true,\n };\n }\n\n response = await sdk.knowledge.create(createData);\n }\n\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Failed to add knowledge entry', response.message);\n process.exitCode = 1;\n return;\n }\n\n const entry = response.data;\n\n if (isJsonOutput()) {\n printJson(entry);\n return;\n }\n\n printBlank();\n if (entry.taskId) {\n printSuccess(`Knowledge entry queued for processing`);\n printLabel('ID', entry.id);\n printLabel('Task ID', entry.taskId);\n printLabel('Status', formatStatus(entry.processingStatus));\n printInfo(`Track progress: chanl kb status ${entry.taskId}`);\n } else {\n printSuccess(`Knowledge entry created`);\n printLabel('ID', entry.id);\n printLabel('Title', entry.title);\n printLabel('Status', formatStatus(entry.processingStatus));\n }\n printBlank();\n } catch (error) {\n spinner.fail('Failed to add knowledge entry');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\nasync function handleKnowledgeUpdate(\n id: string,\n options: {\n title?: string;\n content?: string;\n tags?: string;\n category?: string;\n enabled?: string;\n }\n): Promise<void> {\n const apiKey = configStore.getApiKey();\n if (!apiKey) {\n printError('Not logged in', 'Run: chanl auth login');\n process.exitCode = 1;\n return;\n }\n\n const sdk = new ChanlSDK({\n baseUrl: configStore.getBaseUrl(),\n apiKey,\n });\n\n const spinner = ora('Updating knowledge entry...').start();\n\n try {\n const updateData: any = {};\n if (options.title) updateData.title = options.title;\n if (options.content) updateData.content = options.content;\n if (options.enabled !== undefined) {\n updateData.isEnabled = options.enabled === 'true';\n }\n if (options.tags || options.category) {\n updateData.metadata = {\n ...(options.category && { category: options.category }),\n ...(options.tags && { tags: options.tags.split(',').map((t) => t.trim()) }),\n };\n }\n\n const response = await sdk.knowledge.update(id, updateData);\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Failed to update knowledge entry', response.message);\n process.exitCode = 1;\n return;\n }\n\n // API returns { knowledge: {...} } wrapper\n const responseData = response.data as { knowledge?: Knowledge } | Knowledge;\n const entry = 'knowledge' in responseData && responseData.knowledge ? responseData.knowledge : responseData as Knowledge;\n\n if (isJsonOutput()) {\n printJson(entry);\n return;\n }\n\n printBlank();\n printSuccess('Knowledge entry updated');\n printLabel('ID', entry.id);\n printLabel('Title', entry.title);\n printBlank();\n } catch (error) {\n spinner.fail('Failed to update knowledge entry');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\nasync function handleKnowledgeDelete(\n id: string,\n options: { yes?: boolean }\n): Promise<void> {\n const apiKey = configStore.getApiKey();\n if (!apiKey) {\n printError('Not logged in', 'Run: chanl auth login');\n process.exitCode = 1;\n return;\n }\n\n // Confirmation (unless --yes)\n if (!options.yes) {\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(`Delete knowledge entry ${id}? [y/N] `), resolve);\n });\n rl.close();\n\n if (answer.toLowerCase() !== 'y') {\n printInfo('Cancelled');\n return;\n }\n }\n\n const sdk = new ChanlSDK({\n baseUrl: configStore.getBaseUrl(),\n apiKey,\n });\n\n const spinner = ora('Deleting knowledge entry...').start();\n\n try {\n await sdk.knowledge.delete(id);\n spinner.stop();\n\n if (isJsonOutput()) {\n printJson({ deleted: true, id });\n return;\n }\n\n printBlank();\n printSuccess(`Knowledge entry deleted: ${id}`);\n printBlank();\n } catch (error) {\n spinner.fail('Failed to delete knowledge entry');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\nasync function handleKnowledgeSearch(\n query: string,\n options: {\n mode?: string;\n limit?: string;\n minScore?: string;\n }\n): Promise<void> {\n const apiKey = configStore.getApiKey();\n if (!apiKey) {\n printError('Not logged in', 'Run: chanl auth login');\n process.exitCode = 1;\n return;\n }\n\n const sdk = new ChanlSDK({\n baseUrl: configStore.getBaseUrl(),\n apiKey,\n });\n\n const spinner = ora('Searching knowledge base...').start();\n\n try {\n const searchData: SearchKnowledgeData = {\n query,\n mode: (options.mode as 'hybrid' | 'vector' | 'text') || 'hybrid',\n limit: parseInt(options.limit || '10'),\n minScore: options.minScore ? parseFloat(options.minScore) : undefined,\n };\n\n const response = await sdk.knowledge.search(searchData);\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Search failed', response.message);\n process.exitCode = 1;\n return;\n }\n\n const results = response.data.results || [];\n\n if (isJsonOutput()) {\n printJson(response.data);\n return;\n }\n\n printBlank();\n console.log(chalk.bold(`Search Results (${results.length} matches)`));\n console.log(chalk.dim(`Query: \"${query}\" | Mode: ${options.mode || 'hybrid'}`));\n printBlank();\n\n if (results.length === 0) {\n printInfo('No matching results found');\n return;\n }\n\n results.forEach((result, index) => {\n console.log(\n chalk.cyan(`[${index + 1}]`) +\n ` ${chalk.bold(result.title || 'Untitled')} ` +\n chalk.dim(`(score: ${result.score.toFixed(3)})`)\n );\n console.log(chalk.dim(` ${result.content.slice(0, 150)}...`));\n // API returns 'id' not 'knowledgeId'\n console.log(chalk.dim(` ID: ${(result as any).id || result.knowledgeId}`));\n printBlank();\n });\n } catch (error) {\n spinner.fail('Search failed');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\nasync function handleKnowledgeStatus(taskId: string): Promise<void> {\n const apiKey = configStore.getApiKey();\n if (!apiKey) {\n printError('Not logged in', 'Run: chanl auth login');\n process.exitCode = 1;\n return;\n }\n\n const sdk = new ChanlSDK({\n baseUrl: configStore.getBaseUrl(),\n apiKey,\n });\n\n const spinner = ora('Checking task status...').start();\n\n try {\n const response = await sdk.knowledge.getTaskStatus(taskId);\n spinner.stop();\n\n if (!response.success || !response.data) {\n printError('Task not found', response.message);\n process.exitCode = 1;\n return;\n }\n\n const task = response.data;\n\n if (isJsonOutput()) {\n printJson(task);\n return;\n }\n\n printBlank();\n console.log(chalk.bold('Processing Task Status'));\n printBlank();\n\n printLabel('Task ID', task.id);\n printLabel('Status', formatStatus(task.status));\n printLabel('Progress', `${task.progress}%`);\n if (task.currentStep) printLabel('Current Step', task.currentStep);\n if (task.documentId) printLabel('Document ID', task.documentId);\n if (task.error) printLabel('Error', chalk.red(task.error));\n if (task.result) {\n printLabel('Chunks', task.result.chunkCount.toString());\n printLabel('Words', task.result.wordCount.toString());\n printLabel('Processing Time', `${task.result.processingTimeMs}ms`);\n }\n printBlank();\n } catch (error) {\n spinner.fail('Failed to get task status');\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError('Error', message);\n process.exitCode = 1;\n }\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\nfunction formatStatus(status?: string): string {\n switch (status) {\n case 'completed':\n return chalk.green('completed');\n case 'processing':\n return chalk.yellow('processing');\n case 'pending':\n return chalk.blue('pending');\n case 'failed':\n return chalk.red('failed');\n default:\n return chalk.dim(status || 'unknown');\n }\n}\n"],"mappings":"AAAA,SAAS,eAAe;AACxB,OAAO,SAAS;AAChB,OAAO,WAAW;AAClB,SAAS,cAAc,kBAAkB;AACzC,SAAS,eAAe;AACxB,SAAS,gBAAgB;AAOzB,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAKA,SAAS,yBAAkC;AAChD,QAAM,YAAY,IAAI,QAAQ,IAAI,EAC/B,MAAM,WAAW,EACjB,YAAY,qCAAqC,EACjD;AAAA,IACC;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBF;AAGF,YACG,QAAQ,MAAM,EACd,YAAY,6BAA6B,EACzC,OAAO,wBAAwB,gCAAgC,EAC/D,OAAO,qBAAqB,4CAA4C,EACxE,OAAO,qBAAqB,6BAA6B,EACzD,OAAO,uBAAuB,qBAAqB,EACnD,OAAO,wBAAwB,4BAA4B,IAAI,EAC/D,OAAO,mBAAmB,eAAe,GAAG,EAC5C;AAAA,IACC;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMF,EACC,OAAO,mBAAmB;AAG7B,YACG,QAAQ,UAAU,EAClB,YAAY,6BAA6B,EACzC,OAAO,YAAY,uBAAuB,EAC1C;AAAA,IACC;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKF,EACC,OAAO,kBAAkB;AAG5B,YACG,QAAQ,KAAK,EACb,YAAY,4CAA4C,EACxD,OAAO,uBAAuB,qBAAqB,EACnD,OAAO,oBAAoB,qBAAqB,EAChD,OAAO,eAAe,2BAA2B,EACjD,OAAO,qBAAqB,qCAAqC,EACjE,OAAO,uBAAuB,6BAA6B,EAC3D,OAAO,iBAAiB,sBAAsB,EAC9C,OAAO,yBAAyB,2BAA2B,EAC3D,OAAO,yBAAyB,yBAAyB,GAAG,EAC5D,OAAO,uBAAuB,6BAA6B,IAAI,EAC/D;AAAA,IACC;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF,EACC,OAAO,kBAAkB;AAG5B,YACG,QAAQ,aAAa,EACrB,YAAY,0BAA0B,EACtC,OAAO,uBAAuB,WAAW,EACzC,OAAO,uBAAuB,iCAAiC,EAC/D,OAAO,iBAAiB,sBAAsB,EAC9C,OAAO,yBAAyB,UAAU,EAC1C,OAAO,uBAAuB,2BAA2B,EACzD;AAAA,IACC;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKF,EACC,OAAO,qBAAqB;AAG/B,YACG,QAAQ,aAAa,EACrB,YAAY,0BAA0B,EACtC,OAAO,aAAa,0BAA0B,EAC9C;AAAA,IACC;AAAA,IACA;AAAA;AAAA;AAAA;AAAA,EAIF,EACC,OAAO,qBAAqB;AAG/B,YACG,QAAQ,gBAAgB,EACxB,YAAY,2BAA2B,EACvC,OAAO,qBAAqB,qCAAqC,QAAQ,EACzE,OAAO,wBAAwB,eAAe,IAAI,EAClD,OAAO,uBAAuB,gCAAgC,EAC9D;AAAA,IACC;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUF,EACC,OAAO,qBAAqB;AAG/B,YACG,QAAQ,iBAAiB,EACzB,YAAY,yCAAyC,EACrD;AAAA,IACC;AAAA,IACA;AAAA;AAAA;AAAA,EAGF,EACC,OAAO,qBAAqB;AAE/B,SAAO;AACT;AAMA,eAAe,oBAAoB,SAOjB;AAChB,QAAM,SAAS,YAAY,UAAU;AACrC,MAAI,CAAC,QAAQ;AACX,eAAW,iBAAiB,uBAAuB;AACnD,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,MAAM,IAAI,SAAS;AAAA,IACvB,SAAS,YAAY,WAAW;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,UAAU,IAAI,+BAA+B,EAAE,MAAM;AAE3D,MAAI;AACF,UAAM,UAA4B;AAAA,MAChC,QAAQ,QAAQ;AAAA,MAChB,QAAQ,QAAQ;AAAA,MAChB,kBAAkB,QAAQ;AAAA,MAC1B,UAAU,QAAQ;AAAA,MAClB,OAAO,SAAS,QAAQ,KAAK;AAAA,MAC7B,MAAM,SAAS,QAAQ,IAAI;AAAA,IAC7B;AAEA,UAAM,WAAW,MAAM,IAAI,UAAU,KAAK,OAAO;AACjD,YAAQ,KAAK;AAEb,QAAI,CAAC,SAAS,SAAS;AACrB,iBAAW,oCAAoC,SAAS,OAAO;AAC/D,cAAQ,WAAW;AACnB;AAAA,IACF;AAGA,UAAM,eAAe,SAAS;AAC9B,UAAM,UAAU,cAAc,SAAS,CAAC;AACxC,UAAM,QAAQ,cAAc,SAAS,QAAQ;AAE7C,QAAI,aAAa,GAAG;AAClB,gBAAU,SAAS,IAAI;AACvB;AAAA,IACF;AAEA,QAAI,QAAQ,WAAW,GAAG;AACxB,gBAAU,4BAA4B;AACtC;AAAA,IACF;AAEA,eAAW;AACX,YAAQ,IAAI,MAAM,KAAK,mBAAmB,KAAK,WAAW,CAAC;AAC3D,eAAW;AAEX;AAAA,MACE,CAAC,MAAM,SAAS,UAAU,UAAU,UAAU,SAAS;AAAA,MACvD,QAAQ,IAAI,CAAC,UAAqB;AAAA,QAChC,MAAM,GAAG,MAAM,GAAG,CAAC;AAAA,SAClB,MAAM,SAAS,YAAY,MAAM,GAAG,EAAE;AAAA,QACvC,MAAM;AAAA,QACN,aAAa,MAAM,gBAAgB;AAAA,QACnC,OAAO,MAAM,cAAc,GAAG;AAAA,QAC9B,WAAW,MAAM,SAAS;AAAA,MAC5B,CAAC;AAAA,IACH;AACA,eAAW;AAAA,EACb,SAAS,OAAO;AACd,YAAQ,KAAK,kCAAkC;AAC/C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,eAAW,SAAS,OAAO;AAC3B,YAAQ,WAAW;AAAA,EACrB;AACF;AAEA,eAAe,mBACb,IACA,SACe;AACf,QAAM,SAAS,YAAY,UAAU;AACrC,MAAI,CAAC,QAAQ;AACX,eAAW,iBAAiB,uBAAuB;AACnD,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,MAAM,IAAI,SAAS;AAAA,IACvB,SAAS,YAAY,WAAW;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,UAAU,IAAI,6BAA6B,EAAE,MAAM;AAEzD,MAAI;AACF,UAAM,WAAW,MAAM,IAAI,UAAU,IAAI,EAAE;AAC3C,YAAQ,KAAK;AAEb,QAAI,CAAC,SAAS,WAAW,CAAC,SAAS,MAAM;AACvC,iBAAW,6BAA6B,SAAS,OAAO;AACxD,cAAQ,WAAW;AACnB;AAAA,IACF;AAGA,UAAM,eAAe,SAAS;AAC9B,UAAM,QAAQ,eAAe,gBAAgB,aAAa,YAAY,aAAa,YAAY;AAE/F,QAAI,aAAa,GAAG;AAClB,gBAAU,KAAK;AACf;AAAA,IACF;AAEA,eAAW;AACX,YAAQ,IAAI,MAAM,KAAK,yBAAyB,CAAC;AACjD,eAAW;AAEX,eAAW,MAAM,MAAM,EAAE;AACzB,eAAW,SAAS,MAAM,KAAK;AAC/B,eAAW,UAAU,MAAM,MAAM;AACjC,eAAW,UAAU,aAAa,MAAM,gBAAgB,CAAC;AACzD,QAAI,MAAM,WAAY,YAAW,UAAU,MAAM,WAAW,SAAS,CAAC;AACtE,QAAI,MAAM,WAAY,YAAW,UAAU,MAAM,WAAW,SAAS,CAAC;AACtE,QAAI,MAAM,SAAU,YAAW,UAAU,MAAM,QAAQ;AACvD,QAAI,MAAM,UAAU,SAAU,YAAW,YAAY,MAAM,SAAS,QAAQ;AAC5E,QAAI,MAAM,UAAU,MAAM,QAAQ;AAChC,iBAAW,QAAQ,MAAM,SAAS,KAAK,KAAK,IAAI,CAAC;AAAA,IACnD;AACA,QAAI,MAAM,UAAW,YAAW,WAAW,WAAW,MAAM,SAAS,CAAC;AACtE,QAAI,MAAM,UAAW,YAAW,WAAW,WAAW,MAAM,SAAS,CAAC;AAEtE,QAAI,MAAM,SAAS;AACjB,iBAAW;AACX,cAAQ,IAAI,MAAM,KAAK,kBAAkB,CAAC;AAC1C,cAAQ,IAAI,MAAM,IAAI,MAAM,QAAQ,MAAM,GAAG,GAAG,KAAK,MAAM,QAAQ,SAAS,MAAM,QAAQ,GAAG,CAAC;AAAA,IAChG;AAGA,QAAI,QAAQ,QAAQ;AAClB,YAAM,iBAAiB,MAAM,IAAI,UAAU,UAAU,EAAE;AACvD,UAAI,eAAe,WAAW,eAAe,MAAM;AACjD,mBAAW;AACX,gBAAQ,IAAI,MAAM,KAAK,WAAW,eAAe,KAAK,MAAM,IAAI,CAAC;AACjE,uBAAe,KAAK,MAAM,GAAG,CAAC,EAAE,QAAQ,CAAC,OAAO,MAAM;AACpD,kBAAQ,IAAI,MAAM,IAAI,MAAM,IAAI,CAAC,KAAK,MAAM,QAAQ,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC;AAAA,QACzE,CAAC;AACD,YAAI,eAAe,KAAK,SAAS,GAAG;AAClC,kBAAQ,IAAI,MAAM,IAAI,aAAa,eAAe,KAAK,SAAS,CAAC,OAAO,CAAC;AAAA,QAC3E;AAAA,MACF;AAAA,IACF;AAEA,eAAW;AAAA,EACb,SAAS,OAAO;AACd,YAAQ,KAAK,+BAA+B;AAC5C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,eAAW,SAAS,OAAO;AAC3B,YAAQ,WAAW;AAAA,EACrB;AACF;AAEA,eAAe,mBAAmB,SAUhB;AAChB,QAAM,SAAS,YAAY,UAAU;AACrC,MAAI,CAAC,QAAQ;AACX,eAAW,iBAAiB,uBAAuB;AACnD,YAAQ,WAAW;AACnB;AAAA,EACF;AAGA,QAAM,UAAU,CAAC,QAAQ,MAAM,QAAQ,KAAK,QAAQ,IAAI,EAAE,OAAO,OAAO;AACxE,MAAI,QAAQ,WAAW,GAAG;AACxB,eAAW,mBAAmB,kCAAkC;AAChE,YAAQ,WAAW;AACnB;AAAA,EACF;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,eAAW,oBAAoB,8CAA8C;AAC7E,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,MAAM,IAAI,SAAS;AAAA,IACvB,SAAS,YAAY,WAAW;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,UAAU,IAAI,2BAA2B,EAAE,MAAM;AAEvD,MAAI;AACF,QAAI;AAEJ,QAAI,QAAQ,MAAM;AAEhB,YAAM,WAAW,QAAQ,QAAQ,IAAI;AACrC,UAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,gBAAQ,KAAK,gBAAgB;AAC7B,mBAAW,kBAAkB,QAAQ;AACrC,gBAAQ,WAAW;AACnB;AAAA,MACF;AAEA,YAAM,aAAa,aAAa,QAAQ;AACxC,YAAM,WAAW,QAAQ,KAAK,MAAM,GAAG,EAAE,IAAI,KAAK;AAElD,iBAAW,MAAM,IAAI,UAAU,eAAe,YAAY,UAAU;AAAA,QAClE,OAAO,QAAQ,SAAS;AAAA,QACxB,UAAU,QAAQ;AAAA,QAClB,UAAU;AAAA,UACR,UAAU,QAAQ;AAAA,UAClB,MAAM,QAAQ,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,QACpD;AAAA,MACF,CAAC;AAAA,IACH,OAAO;AAEL,YAAM,aAAkC;AAAA,QACtC,OAAO,QAAQ,UAAU,QAAQ,MAAM,eAAe;AAAA,QACtD,QAAQ,QAAQ,MAAM,QAAQ;AAAA,QAC9B,SAAS,QAAQ;AAAA,QACjB,KAAK,QAAQ;AAAA,QACb,UAAU,QAAQ;AAAA,QAClB,UAAU;AAAA,UACR,UAAU,QAAQ;AAAA,UAClB,MAAM,QAAQ,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,QACpD;AAAA,MACF;AAGA,UAAI,QAAQ,KAAK;AACf,mBAAW,eAAe;AAAA,UACxB,OAAO,SAAS,QAAQ,cAAc,GAAG;AAAA,UACzC,UAAU,SAAS,QAAQ,YAAY,IAAI;AAAA,UAC3C,gBAAgB;AAAA,QAClB;AAAA,MACF;AAEA,iBAAW,MAAM,IAAI,UAAU,OAAO,UAAU;AAAA,IAClD;AAEA,YAAQ,KAAK;AAEb,QAAI,CAAC,SAAS,WAAW,CAAC,SAAS,MAAM;AACvC,iBAAW,iCAAiC,SAAS,OAAO;AAC5D,cAAQ,WAAW;AACnB;AAAA,IACF;AAEA,UAAM,QAAQ,SAAS;AAEvB,QAAI,aAAa,GAAG;AAClB,gBAAU,KAAK;AACf;AAAA,IACF;AAEA,eAAW;AACX,QAAI,MAAM,QAAQ;AAChB,mBAAa,uCAAuC;AACpD,iBAAW,MAAM,MAAM,EAAE;AACzB,iBAAW,WAAW,MAAM,MAAM;AAClC,iBAAW,UAAU,aAAa,MAAM,gBAAgB,CAAC;AACzD,gBAAU,mCAAmC,MAAM,MAAM,EAAE;AAAA,IAC7D,OAAO;AACL,mBAAa,yBAAyB;AACtC,iBAAW,MAAM,MAAM,EAAE;AACzB,iBAAW,SAAS,MAAM,KAAK;AAC/B,iBAAW,UAAU,aAAa,MAAM,gBAAgB,CAAC;AAAA,IAC3D;AACA,eAAW;AAAA,EACb,SAAS,OAAO;AACd,YAAQ,KAAK,+BAA+B;AAC5C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,eAAW,SAAS,OAAO;AAC3B,YAAQ,WAAW;AAAA,EACrB;AACF;AAEA,eAAe,sBACb,IACA,SAOe;AACf,QAAM,SAAS,YAAY,UAAU;AACrC,MAAI,CAAC,QAAQ;AACX,eAAW,iBAAiB,uBAAuB;AACnD,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,MAAM,IAAI,SAAS;AAAA,IACvB,SAAS,YAAY,WAAW;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,UAAU,IAAI,6BAA6B,EAAE,MAAM;AAEzD,MAAI;AACF,UAAM,aAAkB,CAAC;AACzB,QAAI,QAAQ,MAAO,YAAW,QAAQ,QAAQ;AAC9C,QAAI,QAAQ,QAAS,YAAW,UAAU,QAAQ;AAClD,QAAI,QAAQ,YAAY,QAAW;AACjC,iBAAW,YAAY,QAAQ,YAAY;AAAA,IAC7C;AACA,QAAI,QAAQ,QAAQ,QAAQ,UAAU;AACpC,iBAAW,WAAW;AAAA,QACpB,GAAI,QAAQ,YAAY,EAAE,UAAU,QAAQ,SAAS;AAAA,QACrD,GAAI,QAAQ,QAAQ,EAAE,MAAM,QAAQ,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE;AAAA,MAC3E;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,IAAI,UAAU,OAAO,IAAI,UAAU;AAC1D,YAAQ,KAAK;AAEb,QAAI,CAAC,SAAS,WAAW,CAAC,SAAS,MAAM;AACvC,iBAAW,oCAAoC,SAAS,OAAO;AAC/D,cAAQ,WAAW;AACnB;AAAA,IACF;AAGA,UAAM,eAAe,SAAS;AAC9B,UAAM,QAAQ,eAAe,gBAAgB,aAAa,YAAY,aAAa,YAAY;AAE/F,QAAI,aAAa,GAAG;AAClB,gBAAU,KAAK;AACf;AAAA,IACF;AAEA,eAAW;AACX,iBAAa,yBAAyB;AACtC,eAAW,MAAM,MAAM,EAAE;AACzB,eAAW,SAAS,MAAM,KAAK;AAC/B,eAAW;AAAA,EACb,SAAS,OAAO;AACd,YAAQ,KAAK,kCAAkC;AAC/C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,eAAW,SAAS,OAAO;AAC3B,YAAQ,WAAW;AAAA,EACrB;AACF;AAEA,eAAe,sBACb,IACA,SACe;AACf,QAAM,SAAS,YAAY,UAAU;AACrC,MAAI,CAAC,QAAQ;AACX,eAAW,iBAAiB,uBAAuB;AACnD,YAAQ,WAAW;AACnB;AAAA,EACF;AAGA,MAAI,CAAC,QAAQ,KAAK;AAChB,UAAM,WAAW,MAAM,OAAO,UAAU;AACxC,UAAM,KAAK,SAAS,gBAAgB;AAAA,MAClC,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAED,UAAM,SAAS,MAAM,IAAI,QAAgB,CAACA,aAAY;AACpD,SAAG,SAAS,MAAM,OAAO,0BAA0B,EAAE,UAAU,GAAGA,QAAO;AAAA,IAC3E,CAAC;AACD,OAAG,MAAM;AAET,QAAI,OAAO,YAAY,MAAM,KAAK;AAChC,gBAAU,WAAW;AACrB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,IAAI,SAAS;AAAA,IACvB,SAAS,YAAY,WAAW;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,UAAU,IAAI,6BAA6B,EAAE,MAAM;AAEzD,MAAI;AACF,UAAM,IAAI,UAAU,OAAO,EAAE;AAC7B,YAAQ,KAAK;AAEb,QAAI,aAAa,GAAG;AAClB,gBAAU,EAAE,SAAS,MAAM,GAAG,CAAC;AAC/B;AAAA,IACF;AAEA,eAAW;AACX,iBAAa,4BAA4B,EAAE,EAAE;AAC7C,eAAW;AAAA,EACb,SAAS,OAAO;AACd,YAAQ,KAAK,kCAAkC;AAC/C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,eAAW,SAAS,OAAO;AAC3B,YAAQ,WAAW;AAAA,EACrB;AACF;AAEA,eAAe,sBACb,OACA,SAKe;AACf,QAAM,SAAS,YAAY,UAAU;AACrC,MAAI,CAAC,QAAQ;AACX,eAAW,iBAAiB,uBAAuB;AACnD,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,MAAM,IAAI,SAAS;AAAA,IACvB,SAAS,YAAY,WAAW;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,UAAU,IAAI,6BAA6B,EAAE,MAAM;AAEzD,MAAI;AACF,UAAM,aAAkC;AAAA,MACtC;AAAA,MACA,MAAO,QAAQ,QAAyC;AAAA,MACxD,OAAO,SAAS,QAAQ,SAAS,IAAI;AAAA,MACrC,UAAU,QAAQ,WAAW,WAAW,QAAQ,QAAQ,IAAI;AAAA,IAC9D;AAEA,UAAM,WAAW,MAAM,IAAI,UAAU,OAAO,UAAU;AACtD,YAAQ,KAAK;AAEb,QAAI,CAAC,SAAS,WAAW,CAAC,SAAS,MAAM;AACvC,iBAAW,iBAAiB,SAAS,OAAO;AAC5C,cAAQ,WAAW;AACnB;AAAA,IACF;AAEA,UAAM,UAAU,SAAS,KAAK,WAAW,CAAC;AAE1C,QAAI,aAAa,GAAG;AAClB,gBAAU,SAAS,IAAI;AACvB;AAAA,IACF;AAEA,eAAW;AACX,YAAQ,IAAI,MAAM,KAAK,mBAAmB,QAAQ,MAAM,WAAW,CAAC;AACpE,YAAQ,IAAI,MAAM,IAAI,WAAW,KAAK,aAAa,QAAQ,QAAQ,QAAQ,EAAE,CAAC;AAC9E,eAAW;AAEX,QAAI,QAAQ,WAAW,GAAG;AACxB,gBAAU,2BAA2B;AACrC;AAAA,IACF;AAEA,YAAQ,QAAQ,CAAC,QAAQ,UAAU;AACjC,cAAQ;AAAA,QACN,MAAM,KAAK,IAAI,QAAQ,CAAC,GAAG,IACzB,IAAI,MAAM,KAAK,OAAO,SAAS,UAAU,CAAC,MAC1C,MAAM,IAAI,WAAW,OAAO,MAAM,QAAQ,CAAC,CAAC,GAAG;AAAA,MACnD;AACA,cAAQ,IAAI,MAAM,IAAI,OAAO,OAAO,QAAQ,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC;AAE/D,cAAQ,IAAI,MAAM,IAAI,WAAY,OAAe,MAAM,OAAO,WAAW,EAAE,CAAC;AAC5E,iBAAW;AAAA,IACb,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,KAAK,eAAe;AAC5B,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,eAAW,SAAS,OAAO;AAC3B,YAAQ,WAAW;AAAA,EACrB;AACF;AAEA,eAAe,sBAAsB,QAA+B;AAClE,QAAM,SAAS,YAAY,UAAU;AACrC,MAAI,CAAC,QAAQ;AACX,eAAW,iBAAiB,uBAAuB;AACnD,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,MAAM,IAAI,SAAS;AAAA,IACvB,SAAS,YAAY,WAAW;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,UAAU,IAAI,yBAAyB,EAAE,MAAM;AAErD,MAAI;AACF,UAAM,WAAW,MAAM,IAAI,UAAU,cAAc,MAAM;AACzD,YAAQ,KAAK;AAEb,QAAI,CAAC,SAAS,WAAW,CAAC,SAAS,MAAM;AACvC,iBAAW,kBAAkB,SAAS,OAAO;AAC7C,cAAQ,WAAW;AACnB;AAAA,IACF;AAEA,UAAM,OAAO,SAAS;AAEtB,QAAI,aAAa,GAAG;AAClB,gBAAU,IAAI;AACd;AAAA,IACF;AAEA,eAAW;AACX,YAAQ,IAAI,MAAM,KAAK,wBAAwB,CAAC;AAChD,eAAW;AAEX,eAAW,WAAW,KAAK,EAAE;AAC7B,eAAW,UAAU,aAAa,KAAK,MAAM,CAAC;AAC9C,eAAW,YAAY,GAAG,KAAK,QAAQ,GAAG;AAC1C,QAAI,KAAK,YAAa,YAAW,gBAAgB,KAAK,WAAW;AACjE,QAAI,KAAK,WAAY,YAAW,eAAe,KAAK,UAAU;AAC9D,QAAI,KAAK,MAAO,YAAW,SAAS,MAAM,IAAI,KAAK,KAAK,CAAC;AACzD,QAAI,KAAK,QAAQ;AACf,iBAAW,UAAU,KAAK,OAAO,WAAW,SAAS,CAAC;AACtD,iBAAW,SAAS,KAAK,OAAO,UAAU,SAAS,CAAC;AACpD,iBAAW,mBAAmB,GAAG,KAAK,OAAO,gBAAgB,IAAI;AAAA,IACnE;AACA,eAAW;AAAA,EACb,SAAS,OAAO;AACd,YAAQ,KAAK,2BAA2B;AACxC,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,eAAW,SAAS,OAAO;AAC3B,YAAQ,WAAW;AAAA,EACrB;AACF;AAMA,SAAS,aAAa,QAAyB;AAC7C,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,MAAM,MAAM,WAAW;AAAA,IAChC,KAAK;AACH,aAAO,MAAM,OAAO,YAAY;AAAA,IAClC,KAAK;AACH,aAAO,MAAM,KAAK,SAAS;AAAA,IAC7B,KAAK;AACH,aAAO,MAAM,IAAI,QAAQ;AAAA,IAC3B;AACE,aAAO,MAAM,IAAI,UAAU,SAAS;AAAA,EACxC;AACF;","names":["resolve"]}
@@ -0,0 +1,8 @@
1
+ import { Command } from 'commander';
2
+
3
+ /**
4
+ * Create the MCP command group
5
+ */
6
+ declare function createMcpCommand(): Command;
7
+
8
+ export { createMcpCommand };
@@ -0,0 +1,589 @@
1
+ import { Command } from "commander";
2
+ import ora from "ora";
3
+ import chalk from "chalk";
4
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
5
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
6
+ import { configStore } from "../utils/config-store.js";
7
+ import { createSdk } from "../utils/sdk-factory.js";
8
+ import {
9
+ printSuccess,
10
+ printError,
11
+ printInfo,
12
+ printLabel,
13
+ printBlank,
14
+ printSimpleTable,
15
+ isJsonOutput,
16
+ printJson
17
+ } from "../utils/output.js";
18
+ function createMcpCommand() {
19
+ const mcp = new Command("mcp").description("MCP server configuration and status").addHelpText(
20
+ "after",
21
+ `
22
+ What is MCP?
23
+ Model Context Protocol (MCP) lets AI agents (Claude, Cursor, etc.) use your
24
+ Chanl tools, knowledge base, and prompts. The MCP server bridges your workspace
25
+ with AI clients.
26
+
27
+ Quick Start:
28
+ $ chanl mcp # Check server status
29
+ $ chanl mcp test # Run protocol test
30
+ $ chanl mcp config # Get config for your AI client
31
+
32
+ Connecting AI Clients:
33
+ 1. Run 'chanl mcp config --format claude' to get Claude Desktop config
34
+ 2. Add the JSON to Claude Desktop's settings
35
+ 3. Your Chanl tools will appear in Claude!`
36
+ ).action(handleMcpDefault);
37
+ mcp.command("info").description("Show MCP server URL, status, and resource counts").addHelpText(
38
+ "after",
39
+ `
40
+ Example:
41
+ $ chanl mcp info # Show server details
42
+ $ chanl mcp info --json # Output as JSON`
43
+ ).action(handleMcpInfo);
44
+ mcp.command("status").description("Health check the MCP server").addHelpText(
45
+ "after",
46
+ `
47
+ Example:
48
+ $ chanl mcp status # Quick health check`
49
+ ).action(handleMcpStatus);
50
+ mcp.command("test").description("Run MCP protocol test with real MCP client").addHelpText(
51
+ "after",
52
+ `
53
+ Tests the full MCP protocol handshake:
54
+ 1. Connect to MCP server
55
+ 2. List available tools
56
+ 3. List resources (knowledge base)
57
+ 4. List prompts
58
+
59
+ Example:
60
+ $ chanl mcp test # Run all protocol tests
61
+ $ chanl mcp test --json # Output results as JSON`
62
+ ).action(handleMcpTest);
63
+ mcp.command("config").description("Output MCP configuration for AI clients").option("-f, --format <format>", "Format: claude, cursor, generic", "generic").addHelpText(
64
+ "after",
65
+ `
66
+ Generates configuration JSON for connecting AI clients to your MCP server.
67
+
68
+ Supported formats:
69
+ claude - Claude Desktop (claude_desktop_config.json)
70
+ cursor - Cursor IDE settings
71
+ generic - Generic MCP client format
72
+
73
+ Examples:
74
+ $ chanl mcp config # Generic format
75
+ $ chanl mcp config --format claude # Claude Desktop format
76
+ $ chanl mcp config --format cursor # Cursor IDE format
77
+ $ chanl mcp config --format claude > config.json # Save to file
78
+
79
+ Setup Claude Desktop:
80
+ 1. Run: chanl mcp config --format claude
81
+ 2. Copy the JSON output
82
+ 3. Paste into ~/Library/Application Support/Claude/claude_desktop_config.json`
83
+ ).action(handleMcpConfig);
84
+ mcp.command("tools <agent-id>").description("List MCP tools available for an agent").addHelpText(
85
+ "after",
86
+ `
87
+ Example:
88
+ $ chanl mcp tools agent_123 # List tools for agent
89
+ $ chanl mcp tools agent_123 --json # Output as JSON`
90
+ ).action(handleMcpTools);
91
+ mcp.command("call <agent-id> <tool-name> [args]").description("Call an MCP tool by name").addHelpText(
92
+ "after",
93
+ `
94
+ Example:
95
+ $ chanl mcp call agent_123 get_weather '{"city":"London"}'
96
+ $ chanl mcp call agent_123 ping
97
+ $ chanl mcp call agent_123 get_weather '{"city":"London"}' --json`
98
+ ).action(handleMcpCall);
99
+ mcp.command("inspect <agent-id> <tool-name>").description("Show schema and details for a specific MCP tool").addHelpText(
100
+ "after",
101
+ `
102
+ Example:
103
+ $ chanl mcp inspect agent_123 get_weather
104
+ $ chanl mcp inspect agent_123 get_weather --json`
105
+ ).action(handleMcpInspect);
106
+ mcp.command("install").description("Generate MCP config snippet for editors (Cursor, VSCode, Claude)").option("-f, --format <format>", "Format: claude, cursor, vscode, generic", "generic").addHelpText(
107
+ "after",
108
+ `
109
+ Generates a ready-to-use MCP configuration using your stored API key and workspace.
110
+ No server call needed \u2014 works offline.
111
+
112
+ Supported formats:
113
+ claude - Claude Desktop / Claude Code
114
+ cursor - Cursor IDE
115
+ vscode - Visual Studio Code
116
+ generic - Generic MCP client (default)
117
+
118
+ Examples:
119
+ $ chanl mcp install # Generic format
120
+ $ chanl mcp install --format claude # Claude Desktop format
121
+ $ chanl mcp install --format cursor # Cursor IDE format
122
+ $ chanl mcp install --format vscode # VSCode format
123
+ $ chanl mcp install --format claude > mcp.json # Save to file`
124
+ ).action(handleMcpInstall);
125
+ return mcp;
126
+ }
127
+ async function handleMcpInfo() {
128
+ const sdk = createSdk();
129
+ if (!sdk) return;
130
+ const spinner = ora("Fetching MCP configuration...").start();
131
+ try {
132
+ const response = await sdk.mcp.getConfig();
133
+ spinner.stop();
134
+ if (!response.success || !response.data) {
135
+ printError("Failed to fetch MCP configuration", response.message);
136
+ process.exitCode = 1;
137
+ return;
138
+ }
139
+ const config = response.data;
140
+ if (isJsonOutput()) {
141
+ printJson(config);
142
+ return;
143
+ }
144
+ printBlank();
145
+ printSimpleTable(
146
+ ["Property", "Value"],
147
+ [
148
+ ["Server URL", config.serverUrl],
149
+ ["Environment", config.environment],
150
+ ["Status", config.status === "active" ? chalk.green("active") : chalk.red("disabled")],
151
+ ["Tools", config.toolCount.toString()],
152
+ ["Resources", config.resourceCount.toString()],
153
+ ["Prompts", config.promptCount.toString()],
154
+ ...config.toolsetName ? [["Toolset", config.toolsetName]] : []
155
+ ]
156
+ );
157
+ printBlank();
158
+ } catch (error) {
159
+ spinner.fail("Failed to fetch MCP configuration");
160
+ const message = error instanceof Error ? error.message : "Unknown error";
161
+ printError("Error", message);
162
+ process.exitCode = 1;
163
+ }
164
+ }
165
+ async function handleMcpStatus() {
166
+ const sdk = createSdk();
167
+ if (!sdk) return;
168
+ const spinner = ora("Checking MCP server health...").start();
169
+ try {
170
+ const response = await sdk.mcp.getStatus();
171
+ spinner.stop();
172
+ if (!response.success || !response.data) {
173
+ printError("Failed to check MCP status", response.message);
174
+ process.exitCode = 1;
175
+ return;
176
+ }
177
+ const status = response.data;
178
+ if (isJsonOutput()) {
179
+ printJson(status);
180
+ return;
181
+ }
182
+ printBlank();
183
+ if (status.healthy) {
184
+ printSuccess("MCP server is healthy");
185
+ printLabel("Message", status.message);
186
+ if (status.responseTimeMs) {
187
+ printLabel("Response Time", status.responseTimeMs + "ms");
188
+ }
189
+ } else {
190
+ printError("MCP server is unhealthy", status.message);
191
+ if (status.error) {
192
+ printLabel("Error", status.error);
193
+ }
194
+ process.exitCode = 1;
195
+ }
196
+ printBlank();
197
+ } catch (error) {
198
+ spinner.fail("Failed to check MCP status");
199
+ const message = error instanceof Error ? error.message : "Unknown error";
200
+ printError("Error", message);
201
+ process.exitCode = 1;
202
+ }
203
+ }
204
+ async function handleMcpConfig(options) {
205
+ const sdk = createSdk();
206
+ if (!sdk) return;
207
+ const spinner = ora("Fetching MCP configuration...").start();
208
+ try {
209
+ const response = await sdk.mcp.getConfig();
210
+ spinner.stop();
211
+ if (!response.success || !response.data) {
212
+ printError("Failed to fetch MCP configuration", response.message);
213
+ process.exitCode = 1;
214
+ return;
215
+ }
216
+ const config = response.data;
217
+ const format = options.format.toLowerCase();
218
+ let output;
219
+ switch (format) {
220
+ case "claude":
221
+ output = config.agentConfigs.claude;
222
+ break;
223
+ case "cursor":
224
+ output = config.agentConfigs.cursor;
225
+ break;
226
+ case "generic":
227
+ default:
228
+ output = config.agentConfigs.generic;
229
+ break;
230
+ }
231
+ printJson(output);
232
+ if (!isJsonOutput()) {
233
+ printBlank();
234
+ printInfo(`Configuration for ${format} format. Copy and paste into your ${format === "claude" ? "Claude Desktop" : format === "cursor" ? "Cursor IDE" : "MCP client"} configuration.`);
235
+ printInfo(`Replace \${CHANL_MCP_API_KEY} with your actual MCP API key.`);
236
+ }
237
+ } catch (error) {
238
+ spinner.fail("Failed to fetch MCP configuration");
239
+ const message = error instanceof Error ? error.message : "Unknown error";
240
+ printError("Error", message);
241
+ process.exitCode = 1;
242
+ }
243
+ }
244
+ async function handleMcpDefault() {
245
+ const sdk = createSdk();
246
+ if (!sdk) return;
247
+ const spinner = ora("Checking MCP server...").start();
248
+ try {
249
+ const [configResponse, statusResponse] = await Promise.all([
250
+ sdk.mcp.getConfig(),
251
+ sdk.mcp.getStatus()
252
+ ]);
253
+ spinner.stop();
254
+ if (!configResponse.success || !configResponse.data) {
255
+ printError("Failed to fetch MCP configuration", configResponse.message);
256
+ process.exitCode = 1;
257
+ return;
258
+ }
259
+ const config = configResponse.data;
260
+ const status = statusResponse.data;
261
+ if (isJsonOutput()) {
262
+ printJson({ config, status });
263
+ return;
264
+ }
265
+ const statusIcon = status?.healthy ? chalk.green("\u2713") : chalk.red("\u2717");
266
+ const statusText = status?.healthy ? "Connected" : "Unhealthy";
267
+ const latency = status?.responseTimeMs ? ` (${status.responseTimeMs}ms)` : "";
268
+ printBlank();
269
+ console.log(chalk.bold("MCP Server"));
270
+ console.log(chalk.dim("\u2500".repeat(40)));
271
+ printLabel("Server URL", config.serverUrl);
272
+ printLabel("Status", `${statusIcon} ${statusText}${latency}`);
273
+ printLabel("Tools", `${config.toolCount} enabled`);
274
+ printLabel("Resources", config.resourceCount.toString());
275
+ printLabel("Prompts", config.promptCount.toString());
276
+ console.log(chalk.dim("\u2500".repeat(40)));
277
+ printBlank();
278
+ if (!status?.healthy) {
279
+ printInfo("Run 'chanl mcp test' for detailed diagnostics");
280
+ process.exitCode = 1;
281
+ }
282
+ } catch (error) {
283
+ spinner.fail("Failed to check MCP server");
284
+ const message = error instanceof Error ? error.message : "Unknown error";
285
+ printError("Error", message);
286
+ process.exitCode = 1;
287
+ }
288
+ }
289
+ async function handleMcpTools(agentId) {
290
+ const sdk = createSdk();
291
+ if (!sdk) return;
292
+ const spinner = ora("Fetching MCP tools...").start();
293
+ try {
294
+ const response = await sdk.mcp.listTools(agentId);
295
+ spinner.stop();
296
+ if (!response.success || !response.data) {
297
+ printError("Failed to fetch MCP tools", response.message);
298
+ process.exitCode = 1;
299
+ return;
300
+ }
301
+ const tools = response.data.tools;
302
+ if (isJsonOutput()) {
303
+ printJson({ tools });
304
+ return;
305
+ }
306
+ if (tools.length === 0) {
307
+ printBlank();
308
+ printInfo("No MCP tools available for this agent");
309
+ printBlank();
310
+ return;
311
+ }
312
+ printBlank();
313
+ printSimpleTable(
314
+ ["Name", "Description"],
315
+ tools.map((t) => [
316
+ t.name,
317
+ t.description || chalk.dim("No description")
318
+ ])
319
+ );
320
+ printBlank();
321
+ printInfo(`Total: ${tools.length} tool${tools.length === 1 ? "" : "s"}`);
322
+ printBlank();
323
+ } catch (error) {
324
+ spinner.fail("Failed to fetch MCP tools");
325
+ const message = error instanceof Error ? error.message : "Unknown error";
326
+ printError("Error", message);
327
+ process.exitCode = 1;
328
+ }
329
+ }
330
+ async function handleMcpCall(agentId, toolName, argsJson) {
331
+ const sdk = createSdk();
332
+ if (!sdk) return;
333
+ let args;
334
+ if (argsJson) {
335
+ try {
336
+ args = JSON.parse(argsJson);
337
+ } catch {
338
+ printError("Invalid JSON arguments", `Could not parse: ${argsJson}`);
339
+ process.exitCode = 1;
340
+ return;
341
+ }
342
+ }
343
+ const spinner = ora(`Calling tool '${toolName}'...`).start();
344
+ try {
345
+ const response = await sdk.mcp.callTool(agentId, toolName, args);
346
+ spinner.stop();
347
+ if (!response.success || !response.data) {
348
+ printError("Failed to call tool", response.message);
349
+ process.exitCode = 1;
350
+ return;
351
+ }
352
+ const result = response.data;
353
+ if (isJsonOutput()) {
354
+ printJson(result);
355
+ return;
356
+ }
357
+ if (result.isError) {
358
+ for (const item of result.content) {
359
+ if (item.text) {
360
+ printError("Tool returned an error", item.text);
361
+ }
362
+ }
363
+ process.exitCode = 1;
364
+ return;
365
+ }
366
+ printBlank();
367
+ for (const item of result.content) {
368
+ if (item.text) {
369
+ console.log(item.text);
370
+ } else {
371
+ printJson(item);
372
+ }
373
+ }
374
+ printBlank();
375
+ } catch (error) {
376
+ spinner.fail("Failed to call tool");
377
+ const message = error instanceof Error ? error.message : "Unknown error";
378
+ printError("Error", message);
379
+ process.exitCode = 1;
380
+ }
381
+ }
382
+ async function handleMcpInspect(agentId, toolName) {
383
+ const sdk = createSdk();
384
+ if (!sdk) return;
385
+ const spinner = ora(`Fetching tool '${toolName}'...`).start();
386
+ try {
387
+ const response = await sdk.mcp.getTool(agentId, toolName);
388
+ spinner.stop();
389
+ if (!response.success || !response.data) {
390
+ printError("Failed to fetch tool details", response.message);
391
+ process.exitCode = 1;
392
+ return;
393
+ }
394
+ const tool = response.data.tool;
395
+ if (isJsonOutput()) {
396
+ printJson(tool);
397
+ return;
398
+ }
399
+ printBlank();
400
+ console.log(chalk.bold(tool.name));
401
+ console.log(chalk.dim("\u2500".repeat(40)));
402
+ if (tool.description) {
403
+ printLabel("Description", tool.description);
404
+ }
405
+ printBlank();
406
+ console.log(chalk.bold("Input Schema"));
407
+ console.log(chalk.dim("\u2500".repeat(40)));
408
+ const schema = tool.inputSchema;
409
+ const properties = schema["properties"];
410
+ const required = schema["required"] || [];
411
+ if (properties) {
412
+ const rows = [];
413
+ for (const [name, prop] of Object.entries(properties)) {
414
+ const isRequired = required.includes(name);
415
+ const typeStr = String(prop["type"] || "any");
416
+ const desc = String(prop["description"] || "");
417
+ const reqStr = isRequired ? chalk.red("*") : " ";
418
+ rows.push([`${reqStr} ${name}`, typeStr, desc]);
419
+ }
420
+ printSimpleTable(["Parameter", "Type", "Description"], rows);
421
+ } else {
422
+ printInfo("No parameters defined");
423
+ }
424
+ printBlank();
425
+ } catch (error) {
426
+ spinner.fail("Failed to fetch tool details");
427
+ const message = error instanceof Error ? error.message : "Unknown error";
428
+ printError("Error", message);
429
+ process.exitCode = 1;
430
+ }
431
+ }
432
+ async function handleMcpInstall(options) {
433
+ const apiKey = configStore.getApiKey();
434
+ if (!apiKey) {
435
+ printError("Not authenticated", "Run 'chanl login' to authenticate first");
436
+ process.exitCode = 1;
437
+ return;
438
+ }
439
+ const baseUrl = configStore.getBaseUrl() || "https://api.chanl.ai";
440
+ const mcpUrl = baseUrl.replace(/\/$/, "") + "/mcp";
441
+ const format = options.format.toLowerCase();
442
+ const serverConfig = {
443
+ url: mcpUrl,
444
+ headers: { "X-API-Key": apiKey },
445
+ transport: "streamable-http"
446
+ };
447
+ let output;
448
+ switch (format) {
449
+ case "claude":
450
+ output = {
451
+ mcpServers: {
452
+ chanl: serverConfig
453
+ }
454
+ };
455
+ break;
456
+ case "cursor":
457
+ output = {
458
+ mcpServers: {
459
+ chanl: serverConfig
460
+ }
461
+ };
462
+ break;
463
+ case "vscode":
464
+ output = {
465
+ mcpServers: {
466
+ chanl: serverConfig
467
+ }
468
+ };
469
+ break;
470
+ case "generic":
471
+ default:
472
+ output = {
473
+ mcpServers: {
474
+ chanl: serverConfig
475
+ }
476
+ };
477
+ break;
478
+ }
479
+ printJson(output);
480
+ if (!isJsonOutput()) {
481
+ printBlank();
482
+ const targetName = format === "claude" ? "Claude Desktop / Claude Code" : format === "cursor" ? "Cursor IDE" : format === "vscode" ? "Visual Studio Code" : "your MCP client";
483
+ printInfo(`Copy the JSON above into your ${targetName} configuration.`);
484
+ printBlank();
485
+ }
486
+ }
487
+ async function handleMcpTest() {
488
+ const sdk = createSdk();
489
+ if (!sdk) return;
490
+ const spinner = ora("Fetching MCP configuration...").start();
491
+ let config;
492
+ try {
493
+ const configResponse = await sdk.mcp.getConfig();
494
+ if (!configResponse.success || !configResponse.data) {
495
+ spinner.fail("Failed to fetch MCP configuration");
496
+ printError("Error", configResponse.message || "Unknown error");
497
+ process.exitCode = 1;
498
+ return;
499
+ }
500
+ config = configResponse.data;
501
+ } catch (error) {
502
+ spinner.fail("Failed to fetch MCP configuration");
503
+ const message = error instanceof Error ? error.message : "Unknown error";
504
+ printError("Error", message);
505
+ process.exitCode = 1;
506
+ return;
507
+ }
508
+ spinner.text = "Running MCP protocol test...";
509
+ const apiKey = process.env.MCP_API_KEY || configStore.getApiKey();
510
+ if (!apiKey) {
511
+ spinner.fail("No API key available");
512
+ printError("Error", "API key required for MCP test. Set MCP_API_KEY env var for local dev.");
513
+ process.exitCode = 1;
514
+ return;
515
+ }
516
+ const results = [];
517
+ try {
518
+ const transport = new StreamableHTTPClientTransport(new URL(config.serverUrl), {
519
+ requestInit: {
520
+ headers: { "X-API-Key": apiKey }
521
+ }
522
+ });
523
+ const client = new Client(
524
+ { name: "chanl-cli", version: "1.0.0" },
525
+ { capabilities: {} }
526
+ );
527
+ spinner.text = "Connecting to MCP server...";
528
+ const connectStart = Date.now();
529
+ await client.connect(transport);
530
+ const connectTime = Date.now() - connectStart;
531
+ results.push({ test: "Connection", passed: true, detail: `${connectTime}ms` });
532
+ spinner.text = "Testing tools/list...";
533
+ try {
534
+ const tools = await client.listTools();
535
+ results.push({ test: "tools/list", passed: true, detail: `${tools.tools.length} tools` });
536
+ } catch (err) {
537
+ results.push({ test: "tools/list", passed: false, detail: err instanceof Error ? err.message : "Failed" });
538
+ }
539
+ spinner.text = "Testing resources/list...";
540
+ try {
541
+ const resources = await client.listResources();
542
+ results.push({ test: "resources/list", passed: true, detail: `${resources.resources.length} resources` });
543
+ } catch (err) {
544
+ results.push({ test: "resources/list", passed: true, detail: "N/A (no knowledge base)" });
545
+ }
546
+ spinner.text = "Testing prompts/list...";
547
+ try {
548
+ const prompts = await client.listPrompts();
549
+ results.push({ test: "prompts/list", passed: true, detail: `${prompts.prompts.length} prompts` });
550
+ } catch (err) {
551
+ results.push({ test: "prompts/list", passed: true, detail: "N/A" });
552
+ }
553
+ await client.close();
554
+ spinner.stop();
555
+ if (isJsonOutput()) {
556
+ printJson({ serverUrl: config.serverUrl, results });
557
+ return;
558
+ }
559
+ printBlank();
560
+ console.log(chalk.bold("\u{1F9EA} MCP Protocol Test"));
561
+ console.log(chalk.dim("\u2500".repeat(50)));
562
+ printLabel("Server", config.serverUrl);
563
+ console.log(chalk.dim("\u2500".repeat(50)));
564
+ let allPassed = true;
565
+ for (const result of results) {
566
+ const icon = result.passed ? chalk.green("\u2713") : chalk.red("\u2717");
567
+ console.log(`${icon} ${result.test}: ${result.detail}`);
568
+ if (!result.passed) allPassed = false;
569
+ }
570
+ console.log(chalk.dim("\u2500".repeat(50)));
571
+ if (allPassed) {
572
+ printSuccess("All tests passed");
573
+ } else {
574
+ printError("Some tests failed", "Check the results above");
575
+ process.exitCode = 1;
576
+ }
577
+ printBlank();
578
+ } catch (error) {
579
+ spinner.fail("MCP protocol test failed");
580
+ const message = error instanceof Error ? error.message : "Unknown error";
581
+ printError("Connection failed", message);
582
+ printInfo("Make sure the MCP server is running and accessible");
583
+ process.exitCode = 1;
584
+ }
585
+ }
586
+ export {
587
+ createMcpCommand
588
+ };
589
+ //# sourceMappingURL=mcp.js.map