claude-monitor
Version:
Real-time terminal monitoring tool for Claude AI token usage
192 lines • 6.65 kB
JavaScript
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { z } from 'zod';
const SettingsSchema = z.object({
hoursBack: z.number().positive().default(5),
costMode: z.nativeEnum({ AUTO: 'auto', CACHED: 'cached', CALCULATED: 'calculate' }).default('auto'),
planType: z.enum(['pro', 'max5', 'max20', 'custom']).default('custom'),
customLimit: z.number().positive().nullable().default(null),
showNotifications: z.boolean().default(true),
theme: z.enum(['auto', 'light', 'dark']).default('auto'),
timeFormat: z.enum(['12', '24']).default('24'),
timezone: z.string().default('auto'),
updateInterval: z.number().min(1).max(60).default(10),
});
export const CLIArgsSchema = z.object({
plan: z.enum(['pro', 'max5', 'max20', 'custom']).optional(),
timezone: z.string().optional(),
theme: z.enum(['light', 'dark', 'auto']).optional(),
'time-format': z.enum(['12h', '24h', 'auto']).optional(),
'custom-limit': z.number().positive().optional(),
'refresh-rate': z.number().min(1).max(60).optional(),
'refresh-per-second': z.number().min(0.1).max(20.0).optional(),
clear: z.boolean().optional(),
version: z.boolean().optional(),
debug: z.boolean().optional(),
});
export class LastUsedParams {
configDir;
paramsFile;
constructor(configDir) {
this.configDir = configDir || path.join(os.homedir(), '.claude-monitor');
this.paramsFile = path.join(this.configDir, 'last_used.json');
}
save(settings) {
try {
const params = {
theme: settings.theme,
timezone: settings.timezone,
timeFormat: settings.timeFormat,
updateInterval: settings.updateInterval,
timestamp: new Date().toISOString(),
...(settings.customLimit && { customLimit: settings.customLimit }),
};
if (!fs.existsSync(this.configDir)) {
fs.mkdirSync(this.configDir, { recursive: true });
}
const tempFile = `${this.paramsFile}.tmp`;
fs.writeFileSync(tempFile, JSON.stringify(params, null, 2));
fs.renameSync(tempFile, this.paramsFile);
}
catch (error) {
console.warn(`Failed to save last used params: ${error}`);
}
}
load() {
if (!fs.existsSync(this.paramsFile)) {
return {};
}
try {
const content = fs.readFileSync(this.paramsFile, 'utf8');
const params = JSON.parse(content);
delete params.timestamp;
return params;
}
catch (error) {
console.warn(`Failed to load last used params: ${error}`);
return {};
}
}
clear() {
try {
if (fs.existsSync(this.paramsFile)) {
fs.unlinkSync(this.paramsFile);
}
}
catch (error) {
console.warn(`Failed to clear last used params: ${error}`);
}
}
exists() {
return fs.existsSync(this.paramsFile);
}
}
export class SettingsManager {
lastUsedParams;
settings;
constructor() {
this.lastUsedParams = new LastUsedParams();
this.settings = this.getDefaultSettings();
}
getDefaultSettings() {
return {
hoursBack: 5,
costMode: 'auto',
planType: 'custom',
customLimit: null,
showNotifications: true,
theme: 'auto',
timeFormat: this.detectSystemTimeFormat(),
timezone: this.detectSystemTimezone(),
updateInterval: 10,
};
}
detectSystemTimeFormat() {
const date = new Date();
const formatted = date.toLocaleTimeString();
return formatted.includes('AM') || formatted.includes('PM') ? '12' : '24';
}
detectSystemTimezone() {
try {
return Intl.DateTimeFormat().resolvedOptions().timeZone;
}
catch {
return 'UTC';
}
}
loadWithCLI(cliArgs) {
const parsedArgs = CLIArgsSchema.parse(cliArgs);
if (parsedArgs.clear) {
this.lastUsedParams.clear();
return this.settings;
}
const lastUsed = this.lastUsedParams.load();
const merged = {
...this.getDefaultSettings(),
...this.mapLastUsedToSettings(lastUsed),
...this.mapCLIToSettings(parsedArgs),
};
this.settings = SettingsSchema.parse(merged);
if (this.settings.theme === 'auto') {
this.settings.theme = this.detectTerminalTheme();
}
if (this.settings.timezone === 'auto') {
this.settings.timezone = this.detectSystemTimezone();
}
this.lastUsedParams.save(this.settings);
return this.settings;
}
mapLastUsedToSettings(lastUsed) {
const mapped = {};
if (lastUsed.theme)
mapped.theme = lastUsed.theme;
if (lastUsed.timezone)
mapped.timezone = lastUsed.timezone;
if (lastUsed.timeFormat)
mapped.timeFormat = lastUsed.timeFormat;
if (lastUsed.updateInterval)
mapped.updateInterval = lastUsed.updateInterval;
if (lastUsed.customLimit)
mapped.customLimit = lastUsed.customLimit;
return mapped;
}
mapCLIToSettings(args) {
const mapped = {};
if (args.plan)
mapped.planType = args.plan;
if (args.timezone)
mapped.timezone = args.timezone;
if (args.theme)
mapped.theme = args.theme;
if (args['time-format']) {
mapped.timeFormat = args['time-format'] === '12h' ? '12' : '24';
}
if (args['custom-limit'])
mapped.customLimit = args['custom-limit'];
if (args['refresh-rate'])
mapped.updateInterval = args['refresh-rate'];
return mapped;
}
detectTerminalTheme() {
const colorScheme = process.env.COLORFGBG;
if (colorScheme) {
const parts = colorScheme.split(';');
const bg = parts[parts.length - 1];
if (bg && parseInt(bg) < 8) {
return 'dark';
}
}
return 'dark';
}
getSettings() {
return this.settings;
}
updateSettings(updates) {
this.settings = { ...this.settings, ...updates };
this.lastUsedParams.save(this.settings);
return this.settings;
}
}
export const settingsManager = new SettingsManager();
//# sourceMappingURL=settings.js.map