//! Subcommand enums split out of cli.rs (the 500-line cap). //! Clap-derive types only — `crate::cli` re-exports them so callers and //! dispatch keep their import paths. use clap::{Args, Subcommand}; #[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, }, } /// `sm121 config` subcommands — manage the global config file. #[derive(Subcommand)] pub enum ConfigCommands { /// Show the active config path and where the global config lives Path, /// Create the global config file (template) if it does not exist InitGlobal, } #[derive(Args)] pub struct PublishArgs { #[arg(long, required = true)] pub recipe: String, /// Cluster whose fabric env + node count fill the generated run script #[arg(long)] pub cluster: Option, /// Output dir (default: ./publish/) #[arg(long)] pub out: Option, /// Also create a public GitHub gist (requires `gh` CLI, authenticated) #[arg(long)] pub gist: bool, /// After bundling, push the recipe's image to the image registry /// (`[registry]` config) under its version tag + `latest`. #[arg(long)] pub push: bool, /// Node that has the image for `--push` (default: probe all cluster /// nodes for the first match). #[arg(long)] pub node: Option, /// Re-push even if the publication ledger already records this image /// digest (with `--push`). #[arg(long)] pub force: bool, /// Upload the bundle to a recipe registry (see `sm121 registry --help`) #[arg(long, value_name = "URL")] pub registry: Option, /// Registry publish token (or $SM121RECIPES_TOKEN) #[arg(long)] pub token: Option, } #[derive(Subcommand)] pub enum RegistryCommands { /// List recipes on the registry List { #[arg(long, value_name = "URL")] registry: Option, }, /// Print a registry recipe (recipe.toml) Show { name: String, #[arg(long, value_name = "URL")] registry: Option, }, /// Download a recipe into ./recipes// (+ ./docker-images// if it has an image) Install { name: String, #[arg(long, value_name = "URL")] registry: Option, /// Overwrite an existing ./recipes// #[arg(long)] force: bool, }, } #[derive(Subcommand)] pub enum TmuxCommands { /// Launch cluster via 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)] skip_before_serve: bool, }, /// Stop tmux sessions Stop { #[arg(long, required = true)] recipe: String, #[arg(long, default_value = "auto")] cluster: String, }, /// Detailed tmux session status Status { #[arg(long, required = true)] recipe: String, #[arg(long, default_value = "auto")] cluster: String, }, /// Show tmux session logs Logs { #[arg(long, required = true)] recipe: String, #[arg(long, action = clap::ArgAction::SetTrue)] follow: bool, #[arg(long, default_value = "auto")] cluster: String, }, /// Attach to head node tmux session Attach { #[arg(long, required = true)] recipe: String, #[arg(long, default_value = "auto")] cluster: String, }, } /// `sm121 repos` — the configured git recipe repos. `sm121 repos` (no /// subcommand) lists state; `sm121 repos sync` clones/pulls them now. #[derive(clap::Args)] pub struct ReposArgs { #[command(subcommand)] pub command: Option, } #[derive(Clone, clap::Subcommand)] pub enum ReposCommands { /// List the configured repos with their state (cloned? archived? /// last-pull ts? recipe count?) List, /// Clone/pull the configured recipe repos now (`--force` re-clones /// each cached checkout from scratch). `--watch` keeps re-syncing in /// the background (every `--interval` minutes) so the cache never goes /// stale between commands — run it detached (nohup/launchd) the way the /// HA add-on's entrypoint loop does. Sync { #[arg(long)] force: bool, /// Re-sync continuously (background mode; Ctrl-C to stop) #[arg(long)] watch: bool, /// Re-sync interval in minutes (with --watch; default 10) #[arg(long, default_value_t = 10)] interval: u32, }, } #[derive(Args)] pub struct SyncImageArgs { #[arg(long, required = true)] pub recipe: String, #[arg(long, default_value = "auto")] pub cluster: String, /// Explicit source node (default: newest image present in the set). #[arg(long)] pub source: Option, } /// Docker-only launcher flags that the operator actually set — a /// `deploy = "tmux"` serve warns about each one and ignores it. pub fn tmux_unsupported_opts(opts: &crate::docker::ServeOpts) -> Vec<&'static str> { [ (opts.sync_image, "--sync-image"), (opts.sync_image_source.is_some(), "--sync-image-source"), (opts.build_node.is_some(), "--build-node"), (opts.force_build, "--force-build"), (opts.keep, "--keep"), (opts.solo.is_some(), "--solo/--no-solo"), (opts.mirror.is_some(), "--mirror"), (opts.fit.is_some(), "--fit"), ] .iter() .filter_map(|(set, flag)| if *set { Some(*flag) } else { None }) .collect() }