UNPKG

capsule-ai-cli

Version:

The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing

296 lines 12.8 kB
import { configManager } from '../../core/config.js'; import { contextManager } from '../../services/context.js'; import { chatService } from '../../services/chat.js'; import { stateService } from '../../services/state.js'; import { authService } from '../../services/auth.js'; import { toolExecutor } from '../../tools/executor.js'; import { toolResultsService } from '../../services/tool-results.js'; import { ModelResolver } from '../../services/model-resolver.js'; import { openRouterModelsService } from '../../services/openrouter-models.js'; import chalk from 'chalk'; export const taskCommand = { name: 'task', description: 'Execute a task with specified model (micro mode)', alias: ['t', 'micro'], async execute(args) { if (!args || args.length === 0) { return { success: false, message: 'Please provide a task description. Usage: /task "your task" --provider openai --model gpt-4o' }; } const authStatus = await authService.getStatus(); if (!authStatus.isAuthenticated) { return { success: false, message: chalk.yellow('⚠️ Activation Required\n\n') + 'Capsule must be activated to use task mode.\n' + 'Purchase at: ' + chalk.cyan('https://capsulecli.dev') + '\n' + 'Then activate with: ' + chalk.yellow('/activate') }; } const config = configManager.getConfig(); if (!config.providers.openrouter?.apiKey) { return { success: false, message: chalk.yellow('⚠️ OpenRouter API Key Required\n\n') + 'Task mode requires an OpenRouter API key.\n' + 'Get your key at: ' + chalk.cyan('https://openrouter.ai/keys') + '\n' + 'Then set it with: ' + chalk.yellow('/keys') }; } const fullArgs = args.join(' '); const taskMatch = fullArgs.match(/^["'](.+?)["']|^([^-]+)/); const task = taskMatch ? (taskMatch[1] || taskMatch[2]).trim() : ''; if (!task) { return { success: false, message: 'Invalid task format. Wrap your task in quotes or provide it before options.' }; } const options = {}; const optionMatches = fullArgs.matchAll(/--(\w+)(?:\s+([^-]+?)(?=\s*(?:--|$)))?/g); for (const match of optionMatches) { const key = match[1]; const value = match[2]?.trim(); switch (key) { case 'provider': case 'p': options.provider = value; break; case 'model': case 'm': options.model = value; break; case 'context': case 'c': options.context = value; break; case 'json': case 'j': options.json = true; break; case 'timeout': case 't': options.timeout = value ? parseInt(value) : undefined; break; } } let provider = options.provider || stateService.getProvider(); let model = options.model || stateService.getModel(); if (options.provider) { const resolvedProvider = ModelResolver.resolveProvider(options.provider); if (!resolvedProvider) { const availableProviders = openRouterModelsService.getAvailableProviders(); return { success: false, message: chalk.red(`Provider "${options.provider}" not found.\n\n`) + 'Available providers: ' + availableProviders.join(', ') }; } provider = resolvedProvider; } if (options.model) { const resolvedModel = ModelResolver.resolveModel(options.model, provider); if (!resolvedModel) { const suggestions = ModelResolver.getSuggestions(options.model, provider); let message = chalk.red(`Model "${options.model}" not found.\n\n`); if (suggestions.length > 0) { message += 'Did you mean one of these?\n'; suggestions.forEach(s => { message += ` • ${s}\n`; }); } else { const models = await stateService.getAvailableModels(provider); message += `Available models for ${provider}:\n`; models.slice(0, 5).forEach(m => { message += ` • ${m}\n`; }); if (models.length > 5) { message += ` ... and ${models.length - 5} more`; } } return { success: false, message }; } model = resolvedModel; } const validation = ModelResolver.validateModel(model); if (!validation.valid) { return { success: false, message: chalk.red(`Model "${model}" cannot be used: ${validation.reason}`) }; } const taskContext = contextManager.createNewContext(); const previousContext = contextManager.getCurrentContext(); contextManager.setCurrentContext(taskContext.id); try { const originalProvider = stateService.getProvider(); const originalModel = stateService.getModel(); stateService.setProvider(provider); stateService.setModel(model); let prompt = task; if (options.context) { prompt += `\n\nContext: ${options.context}`; } if (!options.json) { const startMessage = '\n' + chalk.cyan('═'.repeat(60)) + '\n' + chalk.bold('Task Execution\n') + chalk.cyan('═'.repeat(60)) + '\n\n' + chalk.gray('Task: ') + task + '\n' + chalk.gray('Provider: ') + provider + '\n' + chalk.gray('Model: ') + model + '\n\n' + chalk.yellow('⏳ Executing...\n'); console.log(startMessage); } const startTime = Date.now(); const result = await executeTask(prompt, options); const duration = ((Date.now() - startTime) / 1000).toFixed(1); stateService.setProvider(originalProvider); stateService.setModel(originalModel); contextManager.setCurrentContext(previousContext.id); if (options.json) { const jsonOutput = { success: result.success, task: task, provider: provider, model: model, duration: `${duration}s`, tokensUsed: result.tokensUsed || 0, summary: result.summary || '', output: result.output || '', toolsExecuted: result.toolsExecuted || [], error: result.error }; return { success: true, message: JSON.stringify(jsonOutput, null, 2) }; } else { let output = '\n' + chalk.cyan('═'.repeat(60)) + '\n'; output += chalk.bold('Task Result\n'); output += chalk.cyan('═'.repeat(60)) + '\n\n'; if (result.success) { output += chalk.green('✓ Task completed successfully\n\n'); output += chalk.gray('Duration: ') + duration + 's\n'; output += chalk.gray('Tokens: ') + (result.tokensUsed || 0) + '\n'; if (result.toolsExecuted && result.toolsExecuted.length > 0) { output += chalk.gray('Tools used: ') + result.toolsExecuted.join(', ') + '\n'; } output += '\n'; if (result.summary) { output += chalk.bold('Summary:\n'); output += result.summary + '\n\n'; } if (result.output && result.output !== result.summary) { output += chalk.bold('Full Output:\n'); const truncated = result.output.length > 1000; output += result.output.substring(0, 1000); if (truncated) { output += chalk.dim('\n... (output truncated, use --json for full output)'); } } } else { output += chalk.red('✗ Task failed\n\n'); output += chalk.gray('Error: ') + (result.error || 'Unknown error'); } output += '\n' + chalk.cyan('═'.repeat(60)); return { success: result.success, message: output }; } } catch (error) { contextManager.setCurrentContext(previousContext.id); return { success: false, message: chalk.red('Task execution failed: ') + (error instanceof Error ? error.message : 'Unknown error') }; } } }; async function executeTask(prompt, options) { try { let fullOutput = ''; let tokensUsed = 0; const toolsExecuted = []; const stream = chatService.stream(prompt, { mode: 'agent' }); let timeoutId; if (options.timeout) { timeoutId = setTimeout(() => { chatService.abort(); }, options.timeout * 1000); } for await (const chunk of stream) { if (chunk.delta) { fullOutput += chunk.delta; } if (chunk.usage) { tokensUsed = chunk.usage.totalTokens; } if (chunk.toolCall) { const toolName = chunk.toolCall.name; if (!toolsExecuted.includes(toolName)) { toolsExecuted.push(toolName); } try { const execution = await toolExecutor.execute(chunk.toolCall); const toolResult = { call_id: chunk.toolCall.id, output: execution.result?.output || execution.result?.error || 'Unknown error', success: execution.state === 'completed', name: chunk.toolCall.name }; toolResultsService.setToolResult(chunk.toolCall.id, toolResult); if (execution.result?.error) { fullOutput += `\n\nTool error (${toolName}): ${execution.result.error}`; } } catch (toolError) { const errorResult = { call_id: chunk.toolCall.id, output: { error: toolError instanceof Error ? toolError.message : String(toolError) }, success: false, name: chunk.toolCall.name }; toolResultsService.setToolResult(chunk.toolCall.id, errorResult); fullOutput += `\n\nTool execution failed (${toolName}): ${toolError}`; } } } if (timeoutId) { clearTimeout(timeoutId); } const firstParagraph = fullOutput.split('\n\n')[0]; const summary = firstParagraph.length > 200 ? firstParagraph.substring(0, 197).trim() + '...' : firstParagraph.trim(); return { success: true, output: fullOutput.trim(), summary: summary, tokensUsed: tokensUsed, toolsExecuted: toolsExecuted }; } catch (error) { if (options.timeout && error instanceof Error && error.message.includes('aborted')) { return { success: false, error: `Task timed out after ${options.timeout} seconds` }; } return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } //# sourceMappingURL=task.js.map