mod app; mod hoststats; mod hotkeys; mod localfs; mod profiles; mod resolve; mod session; mod sftp; mod sshconfig; mod term; mod transport; mod ui; use anyhow::Result; use clap::{Parser, Subcommand}; /// KomiTerm — минималистичный MobaXterm-подобный клиент (SSH/Telnet) на Rust #[derive(Parser, Debug)] #[command(name = "komiterm")] struct Cli { #[command(subcommand)] command: Option, /// Имена сохранённых сессий — каждая откроется в своём табе /// (`komiterm myserver staging-box`). Без аргументов и без subcommand — /// открывается менеджер сессий и запускается один таб. session: Vec, } #[derive(Subcommand, Debug)] enum Command { /// Управление профилями входа (логин + пароль/ключ/agent), /// переиспользуемыми между сессиями. Profile { #[command(subcommand)] action: ProfileAction, }, } #[derive(Subcommand, Debug)] enum ProfileAction { /// Добавить новый профиль (интерактивно спросит логин и способ входа) Add, /// Показать все сохранённые профили List, /// Удалить профиль по имени Remove { name: String }, } #[tokio::main] async fn main() -> Result<()> { let cli = Cli::parse(); if let Some(Command::Profile { action }) = cli.command { return handle_profile_action(action); } let sessions = session::SessionStore::load()?; // Пустой список — не ошибка: app::run покажет список сессий прямо // в TUI (уже в raw-режиме), а не будет спрашивать тут через stdin. let targets = cli .session .iter() .map(|name| { sessions .get(name) .cloned() .ok_or_else(|| anyhow::anyhow!("сессия '{name}' не найдена")) }) .collect::>>()?; let credentials = profiles::CredentialStore::load()?; let ssh_config = sshconfig::SshConfig::load_default()?; app::run(targets, sessions, credentials, ssh_config).await } fn handle_profile_action(action: ProfileAction) -> Result<()> { let mut store = profiles::CredentialStore::load()?; match action { ProfileAction::Add => { let profile = profiles::prompt_new_profile()?; store.profiles.retain(|p| p.name != profile.name); store.profiles.push(profile); store.save()?; println!("Профиль сохранён в {}", profiles::CredentialStore::config_path()?.display()); } ProfileAction::List => { if store.profiles.is_empty() { println!("Профилей пока нет. Добавь через `komiterm profile add`."); } for p in &store.profiles { println!("- {} ({})", p.name, p.username); } } ProfileAction::Remove { name } => { let before = store.profiles.len(); store.profiles.retain(|p| p.name != name); if store.profiles.len() == before { anyhow::bail!("профиль '{name}' не найден"); } store.save()?; println!("Профиль '{name}' удалён."); } } Ok(()) }