import { install } from "./commands/install"; import { reconfigure, repair } from "./commands/reconfigure"; import { doctor } from "./commands/doctor"; import { status } from "./commands/status"; import { diagnosticsCollect } from "./commands/diagnostics"; import { redactConfig, type RedactConfigFormat } from "./commands/redact-config"; import type { InstallOptions, ReconfigureOptions } from "./types/context"; function usage(): never { console.error("Usage:"); console.error(" hy2xs-orchestrator install --package-dir [--config ] [--skip-firewall] [--skip-service-start] [--skip-smoke] [--non-interactive]"); console.error(" hy2xs-orchestrator reconfigure --package-dir [--config ] [--dry-run|--apply] [--skip-firewall] [--skip-service-start] [--skip-smoke]"); console.error(" hy2xs-orchestrator repair --package-dir [--config ] [--skip-firewall] [--skip-service-start] [--skip-smoke]"); console.error(" hy2xs-orchestrator doctor --package-dir [--config ] [--skip-firewall] [--skip-service-start] [--skip-smoke]"); console.error(" hy2xs-orchestrator status --package-dir [--config ] [--skip-firewall] [--skip-service-start] [--skip-smoke]"); console.error(" hy2xs-orchestrator diagnostics collect --package-dir [--config ] [--skip-firewall] [--skip-service-start] [--skip-smoke]"); console.error(" hy2xs-orchestrator redact-config --config [--in-place | --out ] [--format auto|env|yaml]"); console.error(" note: --skip-start is deprecated alias for --skip-service-start --skip-smoke"); process.exit(2); } function parseRedactConfigOptions(args: string[]): { configPath: string; outPath: string; inPlace: boolean; format: RedactConfigFormat; } { let configPath = ""; let outPath = ""; let inPlace = false; let format: RedactConfigFormat = "auto"; for (let i = 0; i < args.length; i += 1) { const arg = args[i]; switch (arg) { case "--config": configPath = takeValue(args, i, arg); i += 1; break; case "--out": outPath = takeValue(args, i, arg); i += 1; break; case "--in-place": inPlace = true; break; case "--format": { const value = takeValue(args, i, arg); if (value !== "auto" && value !== "env" && value !== "yaml") { console.error(`invalid --format value: ${value}`); usage(); } format = value; i += 1; break; } default: console.error(`Unknown argument: ${arg}`); usage(); } } if (!configPath) { console.error("Missing --config"); usage(); } if (inPlace === (outPath !== "")) { console.error("Specify exactly one of --in-place or --out "); usage(); } return { configPath, outPath, inPlace, format }; } function takeValue(args: string[], index: number, flag: string): string { const value = args[index + 1]; if (!value || value.startsWith("--")) { console.error(`Missing value for ${flag}`); usage(); } return value; } function parseInstallOptions(args: string[]): InstallOptions { const options: InstallOptions = { packageDir: "", sourceConfigPath: "", runtimeConfigPath: "/etc/hy2xs/hy2xs.env", nonInteractive: false, skipFirewall: false, skipServiceStart: false, skipSmoke: false }; for (let i = 0; i < args.length; i += 1) { const arg = args[i]; switch (arg) { case "--package-dir": options.packageDir = takeValue(args, i, arg); i += 1; break; case "--non-interactive": options.nonInteractive = true; break; case "--skip-firewall": options.skipFirewall = true; break; case "--skip-service-start": options.skipServiceStart = true; break; case "--skip-smoke": options.skipSmoke = true; break; case "--skip-start": console.error("warning: --skip-start is deprecated; use --skip-service-start and/or --skip-smoke"); options.skipServiceStart = true; options.skipSmoke = true; break; case "--config": options.sourceConfigPath = takeValue(args, i, arg); i += 1; break; default: console.error(`Unknown argument: ${arg}`); usage(); } } if (!options.packageDir) { console.error("Missing --package-dir"); usage(); } if (options.skipSmoke && process.env.HY2XS_BREAK_GLASS !== "1") { console.error("--skip-smoke is break-glass only. Set HY2XS_BREAK_GLASS=1 to continue."); usage(); } return options; } function parseReconfigureOptions(args: string[]): ReconfigureOptions { const options: ReconfigureOptions = { packageDir: "", sourceConfigPath: "/etc/hy2xs/hy2xs.env", runtimeConfigPath: "/etc/hy2xs/hy2xs.env", nonInteractive: false, dryRun: false, apply: false, skipFirewall: false, skipServiceStart: false, skipSmoke: false }; for (let i = 0; i < args.length; i += 1) { const arg = args[i]; switch (arg) { case "--package-dir": options.packageDir = takeValue(args, i, arg); i += 1; break; case "--config": options.sourceConfigPath = takeValue(args, i, arg); i += 1; break; case "--dry-run": options.dryRun = true; break; case "--apply": options.apply = true; break; case "--skip-firewall": options.skipFirewall = true; break; case "--skip-service-start": options.skipServiceStart = true; break; case "--skip-smoke": options.skipSmoke = true; break; case "--skip-start": console.error("warning: --skip-start is deprecated; use --skip-service-start and/or --skip-smoke"); options.skipServiceStart = true; options.skipSmoke = true; break; default: console.error(`Unknown argument: ${arg}`); usage(); } } if (!options.packageDir) { console.error("Missing --package-dir"); usage(); } if (options.dryRun === options.apply) { console.error("Specify exactly one of --dry-run or --apply"); usage(); } if (options.skipSmoke && process.env.HY2XS_BREAK_GLASS !== "1") { console.error("--skip-smoke is break-glass only. Set HY2XS_BREAK_GLASS=1 to continue."); usage(); } return options; } function parseCommonOptions(args: string[]): InstallOptions { return parseInstallOptions(args); } async function main(): Promise { const [command, ...args] = Bun.argv.slice(2); if (command === "install") { await install(parseInstallOptions(args)); return; } if (command === "reconfigure") { await reconfigure(parseReconfigureOptions(args)); return; } if (command === "repair") { const options = parseReconfigureOptions(["--apply", ...args]); await repair(options); return; } if (command === "doctor") { const options = parseReconfigureOptions(["--dry-run", ...args]); await doctor(options); return; } if (command === "status") { await status(parseCommonOptions(args)); return; } if (command === "diagnostics") { const [subcommand, ...rest] = args; if (subcommand !== "collect") { usage(); } await diagnosticsCollect(parseCommonOptions(rest)); return; } if (command === "redact-config") { await redactConfig(parseRedactConfigOptions(args)); return; } usage(); } main().catch((error: unknown) => { const message = error instanceof Error ? error.message : String(error); console.error(`[hy2xs] ERROR: ${message}`); process.exit(1); });