UNPKG

@spyder1211/cc-history

Version:
221 lines 8.56 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.LogParser = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const os = __importStar(require("os")); const PRICING_RATES = { input: 3.00 / 1000000, // $3.00 per 1M tokens output: 15.00 / 1000000, // $15.00 per 1M tokens cacheCreation: 3.75 / 1000000, // $3.75 per 1M tokens cacheRead: 0.30 / 1000000 // $0.30 per 1M tokens }; class LogParser { constructor() { this.claudeDir = path.join(os.homedir(), '.claude'); } /** * 本日のログエントリを取得 */ getTodayEntries() { const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD const projectsDir = path.join(this.claudeDir, 'projects'); if (!fs.existsSync(projectsDir)) { throw new Error('Claude projects directory not found'); } const allEntries = []; const projectDirs = fs.readdirSync(projectsDir, { withFileTypes: true }) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name); for (const projectDir of projectDirs) { const projectPath = path.join(projectsDir, projectDir); const logFiles = fs.readdirSync(projectPath) .filter(file => file.endsWith('.jsonl')); for (const logFile of logFiles) { const logPath = path.join(projectPath, logFile); const entries = this.parseLogFile(logPath, today); allEntries.push(...entries); } } return allEntries.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()); } /** * ログファイルを解析して指定日のエントリを抽出 */ parseLogFile(filePath, targetDate) { const entries = []; try { const content = fs.readFileSync(filePath, 'utf8'); const lines = content.trim().split('\n'); for (const line of lines) { try { const entry = JSON.parse(line); if (entry.timestamp && entry.timestamp.startsWith(targetDate)) { entries.push(entry); } } catch (error) { // 無効なJSON行をスキップ continue; } } } catch (error) { // ファイル読み込みエラーをスキップ console.warn(`Warning: Could not read log file ${filePath}`); } return entries; } /** * ユーザメッセージを会話スレッドに変換 */ buildUserThreads(entries) { const userMessages = entries.filter(entry => entry.type === 'user' && entry.message.role === 'user' && typeof entry.message.content === 'string'); const threads = []; for (const userMsg of userMessages) { const thread = this.buildThread(userMsg, entries); threads.push(thread); } return threads; } /** * 単一のユーザメッセージから会話スレッドを構築 */ buildThread(userMsg, allEntries) { const thread = { user: userMsg, responses: [], tools: 0, totalTokens: { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 }, cost: 0, startTime: new Date(userMsg.timestamp), endTime: new Date(userMsg.timestamp) }; // 会話スレッドを追跡 const visited = new Set(); let currentUuid = userMsg.uuid; while (currentUuid && !visited.has(currentUuid)) { visited.add(currentUuid); const responses = allEntries.filter(entry => entry.parentUuid === currentUuid); for (const response of responses) { if (response.type === 'assistant' && 'usage' in response.message) { thread.responses.push(response); // トークン使用量を集計 const usage = response.message.usage; thread.totalTokens.input += usage.input_tokens || 0; thread.totalTokens.output += usage.output_tokens || 0; thread.totalTokens.cacheCreation += usage.cache_creation_input_tokens || 0; thread.totalTokens.cacheRead += usage.cache_read_input_tokens || 0; // ツール使用回数をカウント if ('content' in response.message && response.message.content && Array.isArray(response.message.content)) { thread.tools += response.message.content.filter(c => c.type === 'tool_use').length; } // 終了時刻を更新 const responseTime = new Date(response.timestamp); if (responseTime > thread.endTime) { thread.endTime = responseTime; } } currentUuid = response.uuid; } } // コストを計算 thread.cost = this.calculateCost(thread.totalTokens); return thread; } /** * コスト計算 */ calculateCost(tokens) { return (tokens.input * PRICING_RATES.input + tokens.output * PRICING_RATES.output + tokens.cacheCreation * PRICING_RATES.cacheCreation + tokens.cacheRead * PRICING_RATES.cacheRead); } /** * 日次統計を計算 */ calculateDailyStats(threads) { const stats = { userMessages: threads.length, totalExchanges: 0, toolsUsed: 0, totalTokens: { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 }, totalCost: 0, startTime: new Date(), endTime: new Date(0) }; if (threads.length === 0) return stats; for (const thread of threads) { stats.totalExchanges += thread.responses.length; stats.toolsUsed += thread.tools; stats.totalTokens.input += thread.totalTokens.input; stats.totalTokens.output += thread.totalTokens.output; stats.totalTokens.cacheCreation += thread.totalTokens.cacheCreation; stats.totalTokens.cacheRead += thread.totalTokens.cacheRead; stats.totalCost += thread.cost; if (thread.startTime < stats.startTime) { stats.startTime = thread.startTime; } if (thread.endTime > stats.endTime) { stats.endTime = thread.endTime; } } return stats; } /** * プロジェクト名を取得 */ getProjectName(cwd) { const parts = cwd.split('/'); return parts[parts.length - 1] || 'unknown'; } } exports.LogParser = LogParser; //# sourceMappingURL=parser.js.map