wraith/src-tauri/src/commands/ssh_commands.rs
Vantz Stockwell 737491d3f0 feat: Phase 2 complete — SSH terminal + frontend UI
Rust SSH service: russh async client, DashMap session registry,
TOFU host key verification, CWD tracking via separate exec channel
(never touches terminal stream), base64 event emission for terminal
I/O. 52/52 tests passing.

Vue 3 frontend: ported from Wails v3 to Tauri v2 — useTerminal
composable with streaming TextDecoder + rAF batching, session store
with multi-connection support, connection store/tree, sidebar, tab
bar, status bar, keyboard shortcuts. All Wails imports replaced
with Tauri API equivalents.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 15:28:18 -04:00

116 lines
2.9 KiB
Rust

//! Tauri commands for SSH session management.
//!
//! All commands are async because russh operations are inherently asynchronous.
//! The SSH service is accessed via `State<AppState>` and the `AppHandle` is
//! used for event emission.
use tauri::{AppHandle, State};
use crate::ssh::session::{AuthMethod, SessionInfo};
use crate::AppState;
/// Connect to an SSH server with password authentication.
///
/// Opens a PTY, starts a shell, and begins streaming output via
/// `ssh:data:{session_id}` events. Returns the session UUID.
#[tauri::command]
pub async fn connect_ssh(
hostname: String,
port: u16,
username: String,
password: String,
cols: u32,
rows: u32,
app_handle: AppHandle,
state: State<'_, AppState>,
) -> Result<String, String> {
state
.ssh
.connect(
app_handle,
&hostname,
port,
&username,
AuthMethod::Password(password),
cols,
rows,
)
.await
}
/// Connect to an SSH server with private key authentication.
///
/// The `private_key_pem` should be the PEM-encoded private key content.
/// `passphrase` is `None` if the key is not encrypted.
///
/// Opens a PTY, starts a shell, and begins streaming output via
/// `ssh:data:{session_id}` events. Returns the session UUID.
#[tauri::command]
pub async fn connect_ssh_with_key(
hostname: String,
port: u16,
username: String,
private_key_pem: String,
passphrase: Option<String>,
cols: u32,
rows: u32,
app_handle: AppHandle,
state: State<'_, AppState>,
) -> Result<String, String> {
state
.ssh
.connect(
app_handle,
&hostname,
port,
&username,
AuthMethod::Key {
private_key_pem,
passphrase,
},
cols,
rows,
)
.await
}
/// Write data to a session's PTY stdin.
///
/// The `data` parameter is a string that will be sent as UTF-8 bytes.
#[tauri::command]
pub async fn ssh_write(
session_id: String,
data: String,
state: State<'_, AppState>,
) -> Result<(), String> {
state.ssh.write(&session_id, data.as_bytes()).await
}
/// Resize the PTY window for a session.
#[tauri::command]
pub async fn ssh_resize(
session_id: String,
cols: u32,
rows: u32,
state: State<'_, AppState>,
) -> Result<(), String> {
state.ssh.resize(&session_id, cols, rows).await
}
/// Disconnect an SSH session — closes the channel and removes it.
#[tauri::command]
pub async fn disconnect_ssh(
session_id: String,
state: State<'_, AppState>,
) -> Result<(), String> {
state.ssh.disconnect(&session_id).await
}
/// List all active SSH sessions (metadata only).
#[tauri::command]
pub async fn list_ssh_sessions(
state: State<'_, AppState>,
) -> Result<Vec<SessionInfo>, String> {
Ok(state.ssh.list_sessions())
}