termcode
Version:
Superior terminal AI coding agent with enterprise-grade security, intelligent error recovery, performance monitoring, and plugin system - Advanced Claude Code alternative
99 lines (98 loc) • 2.72 kB
JavaScript
import { promises as fs } from "node:fs";
import os from "node:os";
import path from "node:path";
import { z } from "zod";
const cfgDir = path.join(os.homedir(), ".termcode");
const cfgPath = path.join(cfgDir, "config.json");
export const ConfigSchema = z.object({
defaultProvider: z.string(),
models: z.record(z.string(), z.object({
chat: z.string().optional(),
embed: z.string().optional()
})),
tools: z.object({
shell: z.boolean(),
git: z.boolean(),
tests: z.union([z.literal("auto"), z.boolean()]),
browser: z.boolean()
}),
routing: z.object({
fallback: z.array(z.string()),
budgetUSDMonthly: z.number()
}),
security: z.object({
permissions: z.object({
tools: z.record(z.string(), z.any()),
rules: z.array(z.any())
})
}).optional()
});
export async function loadConfig() {
try {
await ensureConfigDir();
const raw = await fs.readFile(cfgPath, "utf8");
return ConfigSchema.parse(JSON.parse(raw));
}
catch {
return null;
}
}
export async function saveConfig(cfg) {
try {
// Validate config before saving
const validated = ConfigSchema.parse(cfg);
await ensureConfigDir();
await fs.writeFile(cfgPath, JSON.stringify(validated, null, 2), "utf8");
}
catch (error) {
if (error instanceof z.ZodError) {
throw new Error(`Invalid configuration: ${error.issues.map(i => i.message).join(", ")}`);
}
throw error;
}
}
export async function validateConfig() {
try {
const config = await loadConfig();
if (!config) {
return { valid: false, errors: ["No configuration found"] };
}
ConfigSchema.parse(config);
return { valid: true, errors: [] };
}
catch (error) {
if (error instanceof z.ZodError) {
return {
valid: false,
errors: error.issues.map(i => `${i.path.join(".")}: ${i.message}`)
};
}
return { valid: false, errors: [error instanceof Error ? error.message : "Unknown error"] };
}
}
export async function resetConfig() {
try {
await fs.unlink(cfgPath);
}
catch (error) {
// Ignore if file doesn't exist
if (error.code !== 'ENOENT') {
throw error;
}
}
}
export function configPath() {
return cfgPath;
}
async function ensureConfigDir() {
await fs.mkdir(cfgDir, { recursive: true });
}
export async function configExists() {
try {
await fs.access(cfgPath);
return true;
}
catch {
return false;
}
}