185 lines
4.7 KiB
Rust
185 lines
4.7 KiB
Rust
use clap::{Args, Parser, Subcommand};
|
|
use clap_complete::Shell;
|
|
|
|
/// 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.
|
|
#[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),
|
|
|
|
/// Show detailed status 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 (current machine -> remote machine).
|
|
#[arg(short, long, value_name = "[BIND:]PORT:HOST:PORT")]
|
|
pub local: Option<String>,
|
|
|
|
/// Remote forward (remote machine -> current 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>,
|
|
|
|
/// Jump-host chain, ending at the 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 of the final target.
|
|
#[arg(short, long, value_name = "PORT")]
|
|
pub port: Option<u16>,
|
|
|
|
/// Auto-reconnect on connection drop.
|
|
#[arg(long, num_args = 0..=1, default_missing_value = "true", value_name = "BOOL")]
|
|
pub reconnect: Option<bool>,
|
|
|
|
/// Base delay between reconnection attempts, in seconds.
|
|
#[arg(long = "retry-interval", value_name = "SECONDS")]
|
|
pub retry_interval: Option<u32>,
|
|
|
|
/// Cap on the doubling reconnection delay, in seconds.
|
|
#[arg(long = "backoff-max", value_name = "SECONDS")]
|
|
pub backoff_max: Option<u32>,
|
|
|
|
/// SSH ServerAliveInterval, in seconds.
|
|
#[arg(long, value_name = "SECONDS")]
|
|
pub keepalive: Option<u32>,
|
|
}
|
|
|
|
#[derive(Args)]
|
|
pub struct AddArgs {
|
|
/// Name of 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 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,
|
|
|
|
/// Send SIGKILL immediately instead of SIGTERM with a graceful 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,
|
|
}
|