Files
VolcanicSecretary/src/config.ts

63 lines
2.0 KiB
TypeScript

import path from "node:path";
import type { LogLevel } from "./log.js";
export interface Config {
telegramBotToken: string;
telegramOwnerId: number;
workspaceDir: string;
openaiApiKey?: string;
sttModel: string;
piModel?: string;
timezone: string;
shellPath?: string;
logLevel: LogLevel;
}
export class ConfigError extends Error {}
const LOG_LEVELS: LogLevel[] = ["debug", "info", "warn", "error"];
export function loadConfig(env: NodeJS.ProcessEnv = process.env, cwd: string = process.cwd()): Config {
const telegramBotToken = required(env, "TELEGRAM_BOT_TOKEN");
const ownerRaw = required(env, "TELEGRAM_OWNER_ID");
const telegramOwnerId = Number(ownerRaw);
if (!Number.isInteger(telegramOwnerId) || telegramOwnerId <= 0) {
throw new ConfigError(`TELEGRAM_OWNER_ID must be a positive integer, got "${ownerRaw}"`);
}
const logLevel = (env.LOG_LEVEL?.trim() || "info") as LogLevel;
if (!LOG_LEVELS.includes(logLevel)) {
throw new ConfigError(`LOG_LEVEL must be one of ${LOG_LEVELS.join(", ")}, got "${logLevel}"`);
}
const timezone = env.SECRETARY_TIMEZONE?.trim() || Intl.DateTimeFormat().resolvedOptions().timeZone;
try {
new Intl.DateTimeFormat("en-US", { timeZone: timezone });
} catch {
throw new ConfigError(`SECRETARY_TIMEZONE is not a valid IANA timezone: "${timezone}"`);
}
return {
telegramBotToken,
telegramOwnerId,
workspaceDir: path.resolve(cwd, env.WORKSPACE_DIR?.trim() || "workspace"),
openaiApiKey: blankToUndefined(env.OPENAI_API_KEY),
sttModel: env.STT_MODEL?.trim() || "gpt-4o-transcribe",
piModel: blankToUndefined(env.PI_MODEL),
timezone,
shellPath: blankToUndefined(env.SECRETARY_SHELL),
logLevel,
};
}
function required(env: NodeJS.ProcessEnv, key: string): string {
const value = env[key]?.trim();
if (!value) throw new ConfigError(`${key} is required`);
return value;
}
function blankToUndefined(value: string | undefined): string | undefined {
const trimmed = value?.trim();
return trimmed ? trimmed : undefined;
}