gemini-cost-tracker
Version:
CLI tool to display token usage and costs for Gemini and Vertex AI
157 lines • 5.61 kB
JavaScript
import * as fs from 'fs/promises';
import * as path from 'path';
import * as os from 'os';
import { DEFAULT_CONFIG, FILE_PATHS } from '../../utils/constants.js';
import { ErrorHandler } from '../../utils/errorHandler.js';
import { logger } from '../../utils/logger.js';
export class ConfigManager {
configDir;
configPath;
config = null;
constructor() {
this.configDir = path.join(os.homedir(), FILE_PATHS.CONFIG_DIR);
this.configPath = path.join(this.configDir, FILE_PATHS.CONFIG_FILE);
}
async ensureConfigDir() {
try {
await fs.access(this.configDir);
}
catch {
await fs.mkdir(this.configDir, { recursive: true });
logger.debug('Created config directory', { path: this.configDir });
}
}
async loadConfig() {
if (this.config) {
return this.config;
}
try {
const data = await fs.readFile(this.configPath, 'utf-8');
this.config = JSON.parse(data);
logger.debug('Loaded config from file', { path: this.configPath });
return this.config;
}
catch (error) {
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
// Config file doesn't exist, return default config
this.config = this.getDefaultConfig();
logger.info('Using default configuration');
return this.config;
}
throw ErrorHandler.createFileError(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
}
}
async saveConfig(config) {
try {
await this.ensureConfigDir();
await fs.writeFile(this.configPath, JSON.stringify(config, null, 2));
this.config = config;
logger.info('Configuration saved', { path: this.configPath });
}
catch (error) {
throw ErrorHandler.createFileError(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
}
}
async updateConfig(updates) {
const currentConfig = await this.loadConfig();
const newConfig = { ...currentConfig, ...updates };
await this.saveConfig(newConfig);
return newConfig;
}
getDefaultConfig() {
return {
defaultCurrency: DEFAULT_CONFIG.CURRENCY,
defaultFormat: DEFAULT_CONFIG.FORMAT,
};
}
async getDefaultCurrency() {
const config = await this.loadConfig();
return config.defaultCurrency || DEFAULT_CONFIG.CURRENCY;
}
async getDefaultFormat() {
const config = await this.loadConfig();
return config.defaultFormat || DEFAULT_CONFIG.FORMAT;
}
async getDefaultProject() {
const config = await this.loadConfig();
return config.defaultProject;
}
async setDefaultCurrency(currency) {
await this.updateConfig({ defaultCurrency: currency });
}
async setDefaultFormat(format) {
await this.updateConfig({ defaultFormat: format });
}
async setDefaultProject(project) {
await this.updateConfig({ defaultProject: project });
}
async getGeminiApiKey() {
const config = await this.loadConfig();
return config.apiKeys?.gemini || process.env.GEMINI_API_KEY;
}
async setGeminiApiKey(apiKey) {
const config = await this.loadConfig();
await this.updateConfig({
apiKeys: {
...config.apiKeys,
gemini: apiKey,
},
});
}
async getGcpProjectId() {
const config = await this.loadConfig();
return config.gcpSettings?.projectId || process.env.GCP_PROJECT_ID;
}
async setGcpProjectId(projectId) {
const config = await this.loadConfig();
await this.updateConfig({
gcpSettings: {
...config.gcpSettings,
projectId,
},
});
}
async getGcpKeyFilePath() {
const config = await this.loadConfig();
return config.gcpSettings?.keyFilePath || process.env.GOOGLE_APPLICATION_CREDENTIALS;
}
async setGcpKeyFilePath(keyFilePath) {
const config = await this.loadConfig();
await this.updateConfig({
gcpSettings: {
...config.gcpSettings,
keyFilePath,
},
});
}
async validateConfig() {
try {
const config = await this.loadConfig();
// Check if at least one API key is configured
const hasGeminiKey = config.apiKeys?.gemini || process.env.GEMINI_API_KEY;
const hasGcpProject = config.gcpSettings?.projectId || process.env.GCP_PROJECT_ID;
if (!hasGeminiKey && !hasGcpProject) {
logger.warn('No API keys configured');
return false;
}
return true;
}
catch (error) {
logger.error('Config validation failed', {}, error instanceof Error ? error : undefined);
return false;
}
}
async resetConfig() {
this.config = null;
try {
await fs.unlink(this.configPath);
logger.info('Configuration reset');
}
catch (error) {
if (error instanceof Error && 'code' in error && error.code !== 'ENOENT') {
throw ErrorHandler.createFileError(`Failed to reset config: ${error.message}`);
}
}
}
}
//# sourceMappingURL=configManager.js.map