import { fileExists, readText, writeTextAtomic } from "../lib/fs"; import { info, setOperationContext } from "../lib/log"; import { redactEnv, redactYaml } from "../lib/redaction"; export type RedactConfigFormat = "auto" | "env" | "yaml"; export type RedactConfigOptions = { configPath: string; outPath: string; inPlace: boolean; format: RedactConfigFormat; }; function detectFormat(path: string): Exclude | null { const p = path.toLowerCase(); if (p.endsWith(".env") || p.endsWith("hy2xs.env") || p.endsWith("post-install.env")) { return "env"; } if (p.endsWith(".yaml") || p.endsWith(".yml")) { return "yaml"; } return null; } function resolveFormat(format: RedactConfigFormat, path: string): Exclude { if (format !== "auto") { return format; } const detected = detectFormat(path); if (!detected) { throw new Error(`unable to auto-detect format for ${path}; use --format env|yaml`); } return detected; } export async function redactConfig(options: RedactConfigOptions): Promise { setOperationContext(`redact-config-${Date.now().toString(36)}`); if (!(await fileExists(options.configPath))) { throw new Error(`config file not found: ${options.configPath}`); } const targetPath = options.inPlace ? options.configPath : options.outPath; if (!targetPath) { throw new Error("target path is empty"); } const input = await readText(options.configPath); const format = resolveFormat(options.format, options.configPath); const output = format === "env" ? redactEnv(input) : redactYaml(input); await writeTextAtomic(targetPath, output, { mode: 0o600, owner: "root", group: "root" }); info(`redact-config done: source=${options.configPath} target=${targetPath} format=${format} in_place=${options.inPlace}`); }