- -l/-r/-d now show their actual grammar as the clap value name (<[BIND:]PORT:HOST:PORT>, <[BIND:]PORT>) instead of a generic <SPEC>, matching vmic's convention of showing real syntax in --help. The grammar is no longer also repeated in the flag's help text, since the value name already carries it. - --via changes from a single comma-separated flag to a repeatable one (Vec<String> + value_delimiter = ','), so both `--via a --via b` and `--via a,b` work and can be mixed. commands/mod.rs's edits_from_mapping no longer needs to split the string itself - clap does it. - Trimmed CLI help text that restated grammar/rationale already covered by the value name or by --via being required. - Normalized code comments for tone/format consistency: dropped cross-references to vmic's own internals as justification (this codebase should read as self-contained), removed markdown-style *emphasis* asterisks that don't render in plain comments, tightened several run-on sentences into plain declarative ones, and fixed one comment in wipe.rs that inaccurately described close_instance's force path (it sends SIGKILL, not SIGTERM). - spec/porthole-spec.md's --via row updated to document the repeatable form alongside comma-separated. Cargo.toml/Cargo.lock/atomic.rs also carry an external toml crate bump and formatting pass picked up from the working tree.
185 lines
4.7 KiB
Rust
185 lines
4.7 KiB
Rust
use clap::{Args, Parser, Subcommand};
|
|
use clap_complete::Shell;
|
|
|
|
/// porthole - create and manage named SSH port forwards.
|
|
#[derive(Parser)]
|
|
#[command(name = "porthole", version, about)]
|
|
pub struct Cli {
|
|
#[command(subcommand)]
|
|
pub command: Commands,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum Commands {
|
|
/// Save a new forward profile (doesn't open it).
|
|
#[command(visible_alias = "create", visible_alias = "new")]
|
|
Add(AddArgs),
|
|
|
|
/// Start a saved forward as a supervised background process.
|
|
#[command(visible_alias = "start")]
|
|
Open(OpenArgs),
|
|
|
|
/// Stop a running forward.
|
|
#[command(visible_alias = "stop")]
|
|
Close(CloseArgs),
|
|
|
|
/// Update a saved profile.
|
|
Edit(EditArgs),
|
|
|
|
/// Deep-dive health for one forward.
|
|
Status(StatusArgs),
|
|
|
|
/// List all saved profiles with live status.
|
|
#[command(visible_alias = "ls")]
|
|
List(ListArgs),
|
|
|
|
/// Delete a saved profile.
|
|
#[command(visible_alias = "rm", visible_alias = "delete")]
|
|
Remove(RemoveArgs),
|
|
|
|
/// Close and delete every forward, tracked or not.
|
|
#[command(visible_alias = "reset")]
|
|
Wipe(WipeArgs),
|
|
|
|
/// Generate a shell completion script.
|
|
Completions { shell: Shell },
|
|
|
|
/// Internal: runs the supervisor loop for one profile. Not for direct
|
|
/// use - `open` spawns this itself (spec §3).
|
|
#[command(hide = true, name = "__supervise")]
|
|
Supervise { name: String },
|
|
}
|
|
|
|
/// Shared mapping/connection flags for `add` and `edit` - kept as one
|
|
/// struct (`#[command(flatten)]`ed into both) so the two can never drift.
|
|
#[derive(Args, Default)]
|
|
pub struct MappingArgs {
|
|
/// Local forward: your machine -> remote.
|
|
#[arg(short, long, value_name = "[BIND:]PORT:HOST:PORT")]
|
|
pub local: Option<String>,
|
|
|
|
/// Remote forward: remote -> your machine.
|
|
#[arg(short, long, value_name = "[BIND:]PORT:HOST:PORT")]
|
|
pub remote: Option<String>,
|
|
|
|
/// Dynamic forward (SOCKS proxy).
|
|
#[arg(short, long, value_name = "[BIND:]PORT")]
|
|
pub dynamic: Option<String>,
|
|
|
|
/// SSH hop chain: jump hosts plus the final connection target.
|
|
#[arg(long, value_name = "[USER@]HOST[:PORT]", value_delimiter = ',')]
|
|
pub via: Vec<String>,
|
|
|
|
/// Default user for the target and any hop without one.
|
|
#[arg(short, long, value_name = "USER")]
|
|
pub user: Option<String>,
|
|
|
|
/// Identity file override.
|
|
#[arg(short, long, value_name = "PATH")]
|
|
pub identity: Option<String>,
|
|
|
|
/// SSH port on the final target only.
|
|
#[arg(short, long, value_name = "PORT")]
|
|
pub port: Option<u16>,
|
|
|
|
/// Auto-reconnect on drop.
|
|
#[arg(long, num_args = 0..=1, default_missing_value = "true", value_name = "BOOL")]
|
|
pub reconnect: Option<bool>,
|
|
|
|
/// Base delay between reconnect attempts, in seconds.
|
|
#[arg(long = "retry-interval", value_name = "SECONDS")]
|
|
pub retry_interval: Option<u32>,
|
|
|
|
/// Cap on the doubling reconnect delay, in seconds.
|
|
#[arg(long = "backoff-max", value_name = "SECONDS")]
|
|
pub backoff_max: Option<u32>,
|
|
|
|
/// ServerAliveInterval, in seconds.
|
|
#[arg(long, value_name = "SECONDS")]
|
|
pub keepalive: Option<u32>,
|
|
}
|
|
|
|
#[derive(Args)]
|
|
pub struct AddArgs {
|
|
/// Name for the new profile.
|
|
pub name: String,
|
|
|
|
#[command(flatten)]
|
|
pub mapping: MappingArgs,
|
|
}
|
|
|
|
#[derive(Args)]
|
|
pub struct EditArgs {
|
|
/// Name of the profile to edit.
|
|
pub name: String,
|
|
|
|
#[command(flatten)]
|
|
pub mapping: MappingArgs,
|
|
}
|
|
|
|
#[derive(Args)]
|
|
pub struct OpenArgs {
|
|
/// Name of the profile to open. Ignored (and optional) with --all.
|
|
pub name: Option<String>,
|
|
|
|
/// Run attached in the current shell instead of detaching.
|
|
#[arg(short, long)]
|
|
pub foreground: bool,
|
|
|
|
/// Open without auto-reconnect, regardless of the profile setting.
|
|
#[arg(long)]
|
|
pub once: bool,
|
|
|
|
/// Open every profile with reconnect enabled that isn't already open.
|
|
#[arg(long)]
|
|
pub all: bool,
|
|
}
|
|
|
|
#[derive(Args)]
|
|
pub struct CloseArgs {
|
|
/// Name of the profile to close.
|
|
pub name: String,
|
|
|
|
/// SIGKILL immediately instead of graceful SIGTERM + wait.
|
|
#[arg(long)]
|
|
pub force: bool,
|
|
}
|
|
|
|
#[derive(Args)]
|
|
pub struct StatusArgs {
|
|
/// Name of the profile to inspect.
|
|
pub name: String,
|
|
|
|
/// Machine-readable output.
|
|
#[arg(long)]
|
|
pub json: bool,
|
|
}
|
|
|
|
#[derive(Args)]
|
|
pub struct ListArgs {
|
|
/// Show only currently-open forwards.
|
|
#[arg(long)]
|
|
pub running: bool,
|
|
|
|
/// Machine-readable output.
|
|
#[arg(long)]
|
|
pub json: bool,
|
|
}
|
|
|
|
#[derive(Args)]
|
|
pub struct RemoveArgs {
|
|
/// Name of the profile to delete.
|
|
pub name: String,
|
|
|
|
/// Delete the profile but leave an active instance running untracked.
|
|
#[arg(long = "keep-running")]
|
|
pub keep_running: bool,
|
|
}
|
|
|
|
#[derive(Args)]
|
|
pub struct WipeArgs {
|
|
/// Skip the confirmation prompt.
|
|
#[arg(short, long)]
|
|
pub yes: bool,
|
|
}
|