UNPKG

vibetime

Version:

Track your Claude AI usage and costs. Built on ccusage. See rankings, sync data, and monitor your AI spending. Works with all Claude models.

239 lines (204 loc) 6.65 kB
#!/usr/bin/env node /** * Create fallback ccusage bundle using npm package * This is used when git submodules are not available */ import { mkdirSync, writeFileSync, existsSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const bundleDir = join(__dirname, 'src/lib/ccusage-bundle'); console.log('Creating fallback ccusage bundle using npm package...'); // Create bundle directory if (!existsSync(bundleDir)) { mkdirSync(bundleDir, { recursive: true }); } // Create wrapper that uses npm ccusage package as subprocess const wrapperContent = ` /** * Fallback ccusage bundle wrapper using npm package v15.5.2 * This file provides ccusage functionality through npm package subprocess execution */ import { spawn } from 'node:child_process'; import { join } from 'node:path'; export async function runCcusage(args) { return new Promise((resolve, reject) => { // Use npx to run ccusage from npm package const nodeArgs = ['ccusage', ...args, '--json']; const child = spawn('npx', nodeArgs, { stdio: ['ignore', 'pipe', 'pipe'], shell: false }); let stdout = ''; let stderr = ''; child.stdout.on('data', (data) => { stdout += data.toString(); }); child.stderr.on('data', (data) => { stderr += data.toString(); }); child.on('close', (code) => { if (code !== 0) { reject(new Error(\`ccusage exited with code \${code}: \${stderr}\`)); return; } try { // Try to parse the entire stdout as JSON first const trimmed = stdout.trim(); if (trimmed.startsWith('{') || trimmed.startsWith('[')) { const data = JSON.parse(trimmed); resolve(data); return; } // Fallback: look for JSON in individual lines const lines = stdout.split('\\n'); for (const line of lines) { const lineTrimmed = line.trim(); if (lineTrimmed.startsWith('[') || lineTrimmed.startsWith('{')) { try { const data = JSON.parse(lineTrimmed); resolve(data); return; } catch (lineError) { // Continue to next line continue; } } } resolve([]); } catch (error) { reject(new Error('Failed to parse ccusage output: ' + error.message)); } }); child.on('error', (error) => { reject(new Error('Failed to execute ccusage: ' + error.message)); }); }); } // Export convenience functions export const ccusage = { daily: async (options = {}) => { try { const result = await runCcusage(['daily', ...buildArgs(options)]); // ccusage returns {daily: [...], totals: {...}}, extract the daily array return result && result.daily ? result.daily : []; } catch (error) { console.warn('ccusage daily failed:', error.message); return []; } }, monthly: async (options = {}) => { try { const result = await runCcusage(['monthly', ...buildArgs(options)]); // ccusage returns {monthly: [...], totals: {...}}, extract the monthly array return result && result.monthly ? result.monthly : []; } catch (error) { console.warn('ccusage monthly failed:', error.message); return []; } }, session: async (options = {}) => { try { const result = await runCcusage(['session', ...buildArgs(options)]); // ccusage returns {sessions: [...], totals: {...}}, extract the sessions array return result && result.sessions ? result.sessions : []; } catch (error) { console.warn('ccusage session failed:', error.message); return []; } }, blocks: async (options = {}) => { try { const result = await runCcusage(['blocks', ...buildArgs(options)]); // ccusage returns {blocks: [...], totals: {...}}, extract the blocks array return result && result.blocks ? result.blocks : []; } catch (error) { console.warn('ccusage blocks failed:', error.message); return []; } } }; function buildArgs(options) { const args = []; if (options.since) args.push('--since', options.since); if (options.until) args.push('--until', options.until); if (options.mode) args.push('--mode', options.mode); if (options.dir) args.push('--dir', options.dir); return args; } `; writeFileSync(join(bundleDir, 'index.js'), wrapperContent); // Create TypeScript definitions (same as before) const typeDefsContent = ` /** * TypeScript definitions for ccusage bundle (npm fallback) */ export interface CcusageOptions { since?: string; until?: string; mode?: 'auto' | 'calculate' | 'display'; dir?: string; } export interface TokenUsage { inputTokens: number; outputTokens: number; cacheCreationTokens: number; cacheReadTokens: number; totalTokens: number; } export interface ModelBreakdown extends TokenUsage { modelName: string; cost: number; } export interface DailyUsage extends TokenUsage { date: string; cost: number; modelBreakdowns: ModelBreakdown[]; } export interface MonthlyUsage extends TokenUsage { month: string; cost: number; modelBreakdowns: ModelBreakdown[]; } export interface SessionUsage extends TokenUsage { sessionId: string; projectName: string; startTime: string; endTime: string; duration: string; cost: number; modelBreakdowns: ModelBreakdown[]; } export interface BlockUsage extends TokenUsage { startTime: string; endTime: string; cost: number; isActive?: boolean; modelBreakdowns: ModelBreakdown[]; } export declare function runCcusage(args: string[]): Promise<any>; export declare const ccusage: { daily(options?: CcusageOptions): Promise<DailyUsage[]>; monthly(options?: CcusageOptions): Promise<MonthlyUsage[]>; session(options?: CcusageOptions): Promise<SessionUsage[]>; blocks(options?: CcusageOptions): Promise<BlockUsage[]>; }; `; writeFileSync(join(bundleDir, 'index.d.ts'), typeDefsContent); // Create package.json for the bundle const bundlePackageJson = { name: "@vibetime/ccusage-bundle", version: "1.0.0", private: true, type: "module", main: "./index.js", types: "./index.d.ts", description: "ccusage functionality bundled for vibetime CLI (npm fallback)" }; writeFileSync( join(bundleDir, 'package.json'), JSON.stringify(bundlePackageJson, null, 2) ); console.log('✅ Fallback ccusage bundle created successfully at:', bundleDir); console.log(' Using npm ccusage package for compatibility.');