UNPKG

ccremote

Version:

Claude Code Remote: approve prompts from Discord, auto-continue sessions after quota resets, and schedule quota windows around your workday.

108 lines (106 loc) 4.12 kB
import { existsSync } from "node:fs"; import { resolve } from "node:path"; import { config } from "dotenv"; //#region src/core/config.ts /** * Load configuration from environment variables and .env files * * Priority (highest to lowest): * 1. Environment variables (CCREMOTE_*) * 2. Project .env file (./ccremote.env) * 3. Project .env file (./.env) * 4. Global .env file (~/.ccremote.env) * 5. Default values */ function loadConfig() { loadEnvFiles(); const discordBotToken = getEnvVar("CCREMOTE_DISCORD_BOT_TOKEN"); const discordOwnerId = getEnvVar("CCREMOTE_DISCORD_OWNER_ID"); const discordAuthorizedUsers = parseAuthorizedUsers(getEnvVar("CCREMOTE_DISCORD_AUTHORIZED_USERS") || ""); const monitoringInterval = Number.parseInt(getEnvVar("CCREMOTE_MONITORING_INTERVAL") || "2000", 10); const maxRetries = Number.parseInt(getEnvVar("CCREMOTE_MAX_RETRIES") || "3", 10); const autoRestart = getEnvVar("CCREMOTE_AUTO_RESTART") !== "false"; const discordHealthCheckInterval = Number.parseInt(getEnvVar("CCREMOTE_DISCORD_HEALTH_CHECK_INTERVAL") || "3600000", 10); if (!discordBotToken) throw new Error("Missing required environment variable: CCREMOTE_DISCORD_BOT_TOKEN"); if (!discordOwnerId) throw new Error("Missing required environment variable: CCREMOTE_DISCORD_OWNER_ID"); return { discordBotToken, discordOwnerId, discordAuthorizedUsers, monitoringInterval, maxRetries, autoRestart, discordHealthCheckInterval }; } function loadEnvFiles() { if (process.env.NODE_ENV === "test" && process.env.SKIP_ENV_FILES) return; const envFiles = [ resolve(process.cwd(), "ccremote.env"), resolve(process.cwd(), ".env"), resolve(process.env.HOME || "~", ".ccremote.env") ]; for (let i = envFiles.length - 1; i >= 0; i--) { const envFile = envFiles[i]; if (existsSync(envFile)) { config({ path: envFile, override: false }); console.info(`Loaded environment from: ${envFile}`); } } } function getEnvVar(key) { return process.env[key]; } function parseAuthorizedUsers(value) { if (!value.trim()) return []; return value.split(",").map((id) => id.trim()).filter((id) => id.length > 0); } /** * Validate that the current configuration is valid */ function validateConfig(cfg) { if (!cfg.discordBotToken) throw new Error("Discord bot token is required"); if (!cfg.discordOwnerId) throw new Error("Discord owner ID is required"); if (cfg.monitoringInterval < 1e3) console.warn("Warning: Monitoring interval less than 1000ms may cause performance issues"); } if (import.meta.vitest) { const { beforeEach, describe, it, expect, vi } = await import("./dist-bz1tWxsS.js"); describe("loadConfig", () => { beforeEach(() => { vi.resetModules(); for (const key in process.env) if (key.startsWith("CCREMOTE_")) delete process.env[key]; process.env.NODE_ENV = "test"; process.env.SKIP_ENV_FILES = "true"; }); it("should load configuration from CCREMOTE_ prefixed environment variables", () => { process.env.CCREMOTE_DISCORD_BOT_TOKEN = "test_token"; process.env.CCREMOTE_DISCORD_OWNER_ID = "test_owner"; const config$1 = loadConfig(); expect(config$1.discordBotToken).toBe("test_token"); expect(config$1.discordOwnerId).toBe("test_owner"); }); it("should NOT fall back to non-prefixed environment variables", () => { process.env.DISCORD_BOT_TOKEN = "should_not_use"; process.env.DISCORD_OWNER_ID = "should_not_use"; expect(() => loadConfig()).toThrow("Missing required environment variable: CCREMOTE_DISCORD_BOT_TOKEN"); }); it("should parse authorized users correctly", () => { process.env.CCREMOTE_DISCORD_BOT_TOKEN = "test_token"; process.env.CCREMOTE_DISCORD_OWNER_ID = "test_owner"; process.env.CCREMOTE_DISCORD_AUTHORIZED_USERS = "user1,user2, user3 "; expect(loadConfig().discordAuthorizedUsers).toEqual([ "user1", "user2", "user3" ]); }); it("should throw error for missing required variables", () => { expect(() => loadConfig()).toThrow("Missing required environment variable: CCREMOTE_DISCORD_BOT_TOKEN"); }); }); } //#endregion export { validateConfig as n, loadConfig as t };