use clap::{Parser, Subcommand}; #[derive(Parser)] #[command(author, version, about = "DGX Spark Launcher для vLLM и SGLang")] pub struct Cli { #[arg(long, short, default_value = "config.toml")] pub config: String, #[arg(long, short, help = "Показать команды без выполнения")] pub dry_run: bool, #[command(subcommand)] pub command: Commands, } #[derive(Subcommand)] pub enum Commands { /// Запустить кластер Serve { #[arg(long, required = true)] recipe: String, #[arg(long, default_value = "auto")] cluster: String, /// Keep containers after stop (default: auto-remove on stop) #[arg(long, default_value = "false")] keep: bool, /// Skip pre-serve hooks (cluster-level + recipe-level). #[arg(long, default_value = "false")] skip_before_serve: bool, }, /// Показать логи контейнеров Logs { #[arg(long, required = true)] recipe: String, #[arg(long, action = clap::ArgAction::SetTrue)] follow: bool, }, /// Остановить кластер Stop { #[arg(long, required = true)] recipe: String, }, /// Проверить здоровье кластера Health { #[arg( long, help = "Имя кластера (например, 4nodes-switch). Если не указано - проверяет все." )] cluster: Option, #[arg( long, help = "Показать хост-ключи узлов (для добавления в config.toml)" )] fingerprint: bool, }, /// Проверить и исправить рецепт (doctor mode) Doctor { #[arg(long, required = true, help = "Имя рецепта для проверки")] recipe: String, #[arg( long, default_value = "auto", help = "Кластер (если не auto — использует recipe.cluster)" )] cluster: String, #[arg( long, help = "Автоматически исправить проблемы (создать Dockerfile + собрать образ)" )] repair: bool, }, /// Управление tmux-сессиями (альтернатива Docker) #[command(subcommand)] Tmux(TmuxCommands), /// Auto-discover host keys and write them into config.toml InitHostKeys { #[arg( long, help = "Имя кластера (например, 4nodes-switch). Если не указано — все." )] cluster: Option, }, /// Run an arbitrary shell command on a cluster or single node Exec { /// Cluster name (mutually exclusive with --recipe). #[arg(long, conflicts_with = "recipe")] cluster: Option, /// Recipe name; resolves to the recipe's pinned cluster. #[arg(long, conflicts_with = "cluster")] recipe: Option, /// Target a specific node within the cluster (default: all). #[arg(long)] node: Option, /// Name of an alias from `[exec_aliases]` in config.toml. /// Mutually exclusive with a trailing command. #[arg(long)] alias: Option, /// Command to run (everything after `--`). #[arg(trailing_var_arg = true)] command: Vec, }, /// Power on/off nodes via Home Assistant (smart plugs / Wake-on-LAN). Power { #[command(subcommand)] action: PowerAction, }, } #[derive(Subcommand)] pub enum PowerAction { /// Power off: SSH `sudo shutdown now` + wait + HA `turn_off`. Off { #[arg(long)] cluster: String, /// Target a specific node within the cluster (default: all). #[arg(long)] node: Option, }, /// Power on: HA `turn_on` only (no SSH; the smart plug wakes the machine). On { #[arg(long)] cluster: String, /// Target a specific node within the cluster (default: all). #[arg(long)] node: Option, }, /// Switch clusters: disable nodes from source, enable nodes in target. Switch { /// Current cluster (nodes + entities to shut down). #[arg(long)] from: String, /// Target cluster (nodes + entities to wake up). #[arg(long)] to: String, }, } #[derive(Subcommand)] pub enum TmuxCommands { /// Запустить кластер через tmux Serve { #[arg(long, required = true)] recipe: String, #[arg(long, default_value = "auto")] cluster: String, /// Skip pre-serve hooks (cluster-level + recipe-level). #[arg(long, default_value = "false")] skip_before_serve: bool, }, /// Остановить tmux-сессии Stop { #[arg(long, required = true)] recipe: String, #[arg(long, default_value = "auto")] cluster: String, }, /// Статус tmux-сессий (подробный) Status { #[arg(long, required = true)] recipe: String, #[arg(long, default_value = "auto")] cluster: String, }, /// Показать логи tmux-сессий Logs { #[arg(long, required = true)] recipe: String, #[arg(long, action = clap::ArgAction::SetTrue)] follow: bool, #[arg(long, default_value = "auto")] cluster: String, }, /// Подключиться к tmux-сессии головного узла Attach { #[arg(long, required = true)] recipe: String, #[arg(long, default_value = "auto")] cluster: String, }, }