import { existsSync, readFileSync } from "node:fs" import { homedir } from "node:os" import { join } from "node:path" import type { CheckResult, CheckDefinition, ConfigInfo } from "../types" import { CHECK_IDS, CHECK_NAMES, PACKAGE_NAME } from "../constants" import { parseJsonc, detectConfigFile } from "../../../shared" import { OhMyOpenCodeConfigSchema } from "../../../config" const USER_CONFIG_DIR = join(homedir(), ".config", "opencode") const USER_CONFIG_BASE = join(USER_CONFIG_DIR, `${PACKAGE_NAME}`) const PROJECT_CONFIG_BASE = join(process.cwd(), ".opencode", PACKAGE_NAME) function findConfigPath(): { path: string; format: "json" | "jsonc" } | null { const projectDetected = detectConfigFile(PROJECT_CONFIG_BASE) if (projectDetected.format !== "none") { return { path: projectDetected.path, format: projectDetected.format as "json" | "jsonc" } } const userDetected = detectConfigFile(USER_CONFIG_BASE) if (userDetected.format !== "none") { return { path: userDetected.path, format: userDetected.format as "json" | "jsonc" } } return null } export function validateConfig(configPath: string): { valid: boolean; errors: string[] } { try { const content = readFileSync(configPath, "utf-8") const rawConfig = parseJsonc>(content) const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig) if (!result.success) { const errors = result.error.issues.map( (i) => `${i.path.join(".")}: ${i.message}` ) return { valid: false, errors } } return { valid: true, errors: [] } } catch (err) { return { valid: false, errors: [err instanceof Error ? err.message : "Failed to parse config"], } } } export function getConfigInfo(): ConfigInfo { const configPath = findConfigPath() if (!configPath) { return { exists: false, path: null, format: null, valid: true, errors: [], } } if (!existsSync(configPath.path)) { return { exists: false, path: configPath.path, format: configPath.format, valid: true, errors: [], } } const validation = validateConfig(configPath.path) return { exists: true, path: configPath.path, format: configPath.format, valid: validation.valid, errors: validation.errors, } } export async function checkConfigValidity(): Promise { const info = getConfigInfo() if (!info.exists) { return { name: CHECK_NAMES[CHECK_IDS.CONFIG_VALIDATION], status: "pass", message: "Using default configuration", details: ["No custom config file found (optional)"], } } if (!info.valid) { return { name: CHECK_NAMES[CHECK_IDS.CONFIG_VALIDATION], status: "fail", message: "Configuration has validation errors", details: [ `Path: ${info.path}`, ...info.errors.map((e) => `Error: ${e}`), ], } } return { name: CHECK_NAMES[CHECK_IDS.CONFIG_VALIDATION], status: "pass", message: `Valid ${info.format?.toUpperCase()} config`, details: [`Path: ${info.path}`], } } export function getConfigCheckDefinition(): CheckDefinition { return { id: CHECK_IDS.CONFIG_VALIDATION, name: CHECK_NAMES[CHECK_IDS.CONFIG_VALIDATION], category: "configuration", check: checkConfigValidity, critical: false, } }