UNPKG

cortexweaver

Version:

CortexWeaver is a command-line interface (CLI) tool that orchestrates a swarm of specialized AI agents, powered by Claude Code and Gemini CLI, to assist in software development. It transforms a high-level project plan (plan.md) into a series of coordinate

247 lines 9.88 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClaudeClient = exports.ClaudeModel = void 0; const sdk_1 = __importDefault(require("@anthropic-ai/sdk")); var ClaudeModel; (function (ClaudeModel) { ClaudeModel["OPUS"] = "claude-3-opus-20240229"; ClaudeModel["SONNET"] = "claude-3-sonnet-20240229"; ClaudeModel["HAIKU"] = "claude-3-haiku-20240307"; })(ClaudeModel || (exports.ClaudeModel = ClaudeModel = {})); // Pricing per 1M tokens (as of 2024) const MODEL_PRICING = { [ClaudeModel.OPUS]: { input: 15.00, output: 75.00 }, [ClaudeModel.SONNET]: { input: 3.00, output: 15.00 }, [ClaudeModel.HAIKU]: { input: 0.25, output: 1.25 } }; class ClaudeClient { constructor(config) { if (!config.apiKey && !config.sessionToken) { throw new Error('Either API key or session token is required'); } this.config = { apiKey: config.apiKey, sessionToken: config.sessionToken, defaultModel: config.defaultModel || ClaudeModel.SONNET, maxTokens: config.maxTokens || 4096, temperature: config.temperature || 0.7, budgetLimit: config.budgetLimit || Infinity, budgetWarningThreshold: config.budgetWarningThreshold || 0.8 }; // Use session token if available, otherwise use API key if (config.sessionToken) { if (config.sessionToken === 'claude-code-inherited') { // We're running in Claude Code environment, use the inherited authentication this.anthropic = this.createClaudeCodeInheritedClient(); } else { // For manual session tokens, use custom client this.anthropic = this.createSessionAwareAnthropicClient(config.sessionToken); } } else { this.anthropic = new sdk_1.default({ apiKey: this.config.apiKey }); } this.tokenUsage = { totalInputTokens: 0, totalOutputTokens: 0, totalTokens: 0, requestCount: 0, estimatedCost: 0 }; } getConfiguration() { return { ...this.config }; } updateConfiguration(updates) { if (updates.temperature !== undefined) { if (updates.temperature < 0 || updates.temperature > 1) { throw new Error('Temperature must be between 0 and 1'); } } if (updates.maxTokens !== undefined) { if (updates.maxTokens <= 0) { throw new Error('Max tokens must be positive'); } } Object.assign(this.config, updates); } async sendMessage(message, options = {}) { await this.checkBudgetLimit(); const model = options.model || this.config.defaultModel; const maxTokens = options.maxTokens || this.config.maxTokens; const temperature = options.temperature || this.config.temperature; const maxRetries = options.maxRetries || 0; const retryDelay = options.retryDelay || 1000; const messages = [ ...(options.conversationHistory || []), { role: 'user', content: message } ]; const requestPayload = { model, max_tokens: maxTokens, temperature, messages }; if (options.systemPrompt) { requestPayload.system = options.systemPrompt; } let lastError; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const response = await this.anthropic.messages.create(requestPayload); if (!response || !response.content || !Array.isArray(response.content)) { throw new Error('Invalid response format'); } const textContent = response.content .filter(block => 'text' in block) .map(block => block.text) .join(''); const tokenUsage = { inputTokens: response.usage?.input_tokens || 0, outputTokens: response.usage?.output_tokens || 0, totalTokens: (response.usage?.input_tokens || 0) + (response.usage?.output_tokens || 0) }; this.updateTokenUsage(tokenUsage, model); return { content: textContent, tokenUsage, model: response.model }; } catch (error) { lastError = error; if (attempt < maxRetries && this.isRetryableError(error)) { await this.delay(retryDelay); continue; } throw error; } } throw lastError; } async sendMessageStream(message, options = {}) { await this.checkBudgetLimit(); const model = options.model || this.config.defaultModel; const maxTokens = options.maxTokens || this.config.maxTokens; const temperature = options.temperature || this.config.temperature; const messages = [ ...(options.conversationHistory || []), { role: 'user', content: message } ]; const requestPayload = { model, max_tokens: maxTokens, temperature, messages }; if (options.systemPrompt) { requestPayload.system = options.systemPrompt; } const stream = await this.anthropic.messages.stream(requestPayload); return { [Symbol.asyncIterator]: async function* () { for await (const chunk of stream) { if (chunk.type === 'content_block_delta' && 'text' in chunk.delta) { yield chunk.delta.text; } } } }; } getTokenUsage() { return { ...this.tokenUsage }; } resetTokenUsage() { this.tokenUsage = { totalInputTokens: 0, totalOutputTokens: 0, totalTokens: 0, requestCount: 0, estimatedCost: 0 }; } setDefaultModel(model) { if (!Object.values(ClaudeModel).includes(model)) { throw new Error('Invalid model'); } this.config.defaultModel = model; } getAvailableModels() { return Object.values(ClaudeModel); } updateTokenUsage(usage, model) { this.tokenUsage.totalInputTokens += usage.inputTokens; this.tokenUsage.totalOutputTokens += usage.outputTokens; this.tokenUsage.totalTokens += usage.totalTokens; this.tokenUsage.requestCount += 1; // Calculate cost const pricing = MODEL_PRICING[model]; const inputCost = (usage.inputTokens / 1000000) * pricing.input; const outputCost = (usage.outputTokens / 1000000) * pricing.output; this.tokenUsage.estimatedCost += inputCost + outputCost; // Check budget warning if (this.config.budgetLimit < Infinity) { const budgetUsedRatio = this.tokenUsage.estimatedCost / this.config.budgetLimit; if (budgetUsedRatio >= this.config.budgetWarningThreshold && budgetUsedRatio < 1) { console.warn(`Budget warning: ${(budgetUsedRatio * 100).toFixed(1)}% of budget used`); } } } async checkBudgetLimit() { if (this.config.budgetLimit < Infinity && this.tokenUsage.estimatedCost >= this.config.budgetLimit) { throw new Error('Budget limit exceeded'); } } isRetryableError(error) { const errorMessage = error?.message?.toLowerCase() || ''; return errorMessage.includes('rate limit') || errorMessage.includes('timeout') || errorMessage.includes('server error'); } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Create an Anthropic client that inherits Claude Code's authentication */ createClaudeCodeInheritedClient() { // When running in Claude Code environment, create a placeholder client // In a real implementation, this would need to integrate with Claude Code's authentication // For now, this signals that session authentication is available return new sdk_1.default({ apiKey: 'sk-ant-api03-placeholder' // Placeholder that follows Anthropic's API key format }); } /** * Create a session-aware Anthropic client that uses Claude Code session tokens */ createSessionAwareAnthropicClient(sessionToken) { // Create a custom fetch function that adds session token authentication const customFetch = async (url, options) => { const headers = new Headers(options?.headers); // Add Claude Code session authentication headers.set('Cookie', `sessionKey=${sessionToken}`); headers.set('Content-Type', 'application/json'); // Remove any existing Authorization header since we're using session cookies headers.delete('Authorization'); const modifiedOptions = { ...options, headers }; return fetch(url, modifiedOptions); }; return new sdk_1.default({ apiKey: 'session-token-placeholder', // Required by SDK but not used fetch: customFetch, baseURL: 'https://api.anthropic.com' }); } } exports.ClaudeClient = ClaudeClient; //# sourceMappingURL=claude-client.js.map