termcode
Version:
Superior terminal AI coding agent with enterprise-grade security, intelligent error recovery, performance monitoring, and plugin system - Advanced Claude Code alternative
118 lines (117 loc) • 4.28 kB
JavaScript
import { promises as fs } from "node:fs";
import path from "node:path";
import os from "node:os";
const usageDir = path.join(os.homedir(), ".termcode", "usage");
// Ensure usage directory exists
async function ensureUsageDir() {
try {
await fs.mkdir(usageDir, { recursive: true });
}
catch (error) {
// Ignore if already exists
}
}
// Get the usage file path for a given month
function getUsageFilePath(year, month) {
const monthStr = month.toString().padStart(2, '0');
return path.join(usageDir, `${year}-${monthStr}.json`);
}
// Get current month's usage file path
function getCurrentUsageFilePath() {
const now = new Date();
return getUsageFilePath(now.getFullYear(), now.getMonth() + 1);
}
// Load usage records for a specific month
export async function loadMonthlyUsage(year, month) {
try {
await ensureUsageDir();
const filePath = getUsageFilePath(year, month);
const content = await fs.readFile(filePath, 'utf8');
return JSON.parse(content);
}
catch (error) {
// Return empty array if file doesn't exist or can't be read
return [];
}
}
// Load current month's usage records
export async function getCurrentMonthUsage() {
const now = new Date();
return loadMonthlyUsage(now.getFullYear(), now.getMonth() + 1);
}
// Save a usage record
export async function saveUsageRecord(record) {
await ensureUsageDir();
const recordDate = new Date(record.timestamp);
const year = recordDate.getFullYear();
const month = recordDate.getMonth() + 1;
const filePath = getUsageFilePath(year, month);
// Load existing records
const existingRecords = await loadMonthlyUsage(year, month);
// Add new record
existingRecords.push(record);
// Save updated records
await fs.writeFile(filePath, JSON.stringify(existingRecords, null, 2), 'utf8');
}
// Calculate total spending for current month
export async function getCurrentMonthSpending() {
const records = await getCurrentMonthUsage();
return records.reduce((total, record) => total + record.costUSD, 0);
}
// Calculate spending for a specific month
export async function getMonthSpending(year, month) {
const records = await loadMonthlyUsage(year, month);
return records.reduce((total, record) => total + record.costUSD, 0);
}
// Get usage statistics for current month
export async function getCurrentMonthStats() {
const records = await getCurrentMonthUsage();
const stats = {
totalRecords: records.length,
totalCost: 0,
totalTokens: 0,
byProvider: {},
byModel: {}
};
for (const record of records) {
stats.totalCost += record.costUSD;
stats.totalTokens += record.totalTokens;
// Provider stats
if (!stats.byProvider[record.provider]) {
stats.byProvider[record.provider] = { cost: 0, tokens: 0, calls: 0 };
}
stats.byProvider[record.provider].cost += record.costUSD;
stats.byProvider[record.provider].tokens += record.totalTokens;
stats.byProvider[record.provider].calls += 1;
// Model stats
const modelKey = `${record.provider}/${record.model}`;
if (!stats.byModel[modelKey]) {
stats.byModel[modelKey] = { cost: 0, tokens: 0, calls: 0 };
}
stats.byModel[modelKey].cost += record.costUSD;
stats.byModel[modelKey].tokens += record.totalTokens;
stats.byModel[modelKey].calls += 1;
}
return stats;
}
// Clean up old usage files (keep last 12 months)
export async function cleanupOldUsage() {
try {
await ensureUsageDir();
const files = await fs.readdir(usageDir);
const now = new Date();
const cutoffDate = new Date(now.getFullYear(), now.getMonth() - 11, 1); // 12 months ago
for (const file of files) {
if (!file.match(/^\d{4}-\d{2}\.json$/))
continue;
const [year, month] = file.replace('.json', '').split('-').map(Number);
const fileDate = new Date(year, month - 1, 1);
if (fileDate < cutoffDate) {
await fs.unlink(path.join(usageDir, file));
}
}
}
catch (error) {
// Ignore cleanup errors
}
}