Root cause: The output reader loop held Arc<TokioMutex<Channel>> while
calling ch.wait().await. After the initial prompt rendered and the server
went idle, wait() blocked indefinitely holding the lock. ssh_write()
could never acquire the mutex to send keystrokes. Permanent deadlock.
Fix: Separated read/write paths. The output loop now owns the Channel
exclusively via tokio::select!, receiving resize/shutdown commands through
an mpsc channel. Writes go through Handle::data(channel_id, data) which
bypasses the Channel entirely — no shared mutex, no deadlock.
Also killed all compiler warnings (unused imports in rdp module).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Clipboard paste (rdp_send_clipboard): simulates typing each character
via scancode key press/release events. Full ASCII coverage including
all symbols, numbers, and shifted characters. Handles 32-char
generated passwords without manual typing.
2. Keyboard grab defaults to ON so RDP sessions accept keyboard input
immediately without requiring the user to click the toolbar toggle.
3. Frame dirty flag: GraphicsUpdate sets an AtomicBool, get_frame only
encodes + returns base64 when dirty (returns empty string otherwise).
Eliminates ~8MB/frame base64 encoding on unchanged frames at 30fps.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ConnectionRecord.tags changed from String to Vec<String> so the
frontend receives a proper array instead of a raw JSON string.
The old behavior caused v-for to iterate characters, corrupting
the connection display in the sidebar.
- DevTools now only auto-opens in debug builds (cfg(debug_assertions)),
not in production.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend cleanup (Gemini):
- Strip verbose doc comments across SSH, RDP, and command modules
- Add 10s timeout on SSH connect/auth, 15s on RDP connection
- Fix macOS data directory to ~/Library/Application Support/Wraith
- Add generic disconnect_session command
- Simplify SFTP setup and error handling
- Inline AppState field construction
Gemini AI XO integration:
- Add GeminiService (src-tauri/src/ai/) with API Key, Service Account,
and Google Account (OAuth2) authentication methods
- Add ai_commands (set_gemini_auth, gemini_chat, is_gemini_authenticated)
- Add GeminiPanel.vue — collapsible chat sidebar with multi-auth UI
- Wire Ctrl+Shift+G toggle and status bar AI button in MainLayout
- Add reqwest + anyhow dependencies
Bugfix:
- Fix dropped modulo operator in Ctrl+Tab/Ctrl+Shift+Tab handlers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove redundant doc comments and section headers across SSH, RDP, and command modules
- Add 10s timeout on SSH connect/auth, 15s timeout on RDP connection
- Fix macOS data directory to use ~/Library/Application Support/Wraith
- Add generic disconnect_session command alongside disconnect_ssh
- Simplify SFTP setup and RDP error handling
- Add explicit label/url to main window config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added devtools Cargo feature, auto-open DevTools on startup
so we can see frontend console errors on Windows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added decrypt_password and decrypt_ssh_key Tauri commands.
Connect flow now resolves credentialId → decrypted credentials
from the vault. Falls back to window.prompt on auth failure.
Fixed case-sensitive error string matching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Writes to %APPDATA%\Wraith\wraith-startup.log since release
builds suppress all console output via windows_subsystem.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rust RDP service: ironrdp client with full connection handshake
(TCP -> TLS -> CredSSP -> NLA), pixel buffer frame delivery,
mouse/keyboard input via scancode mapping, graceful disconnect.
Runs in dedicated thread with own tokio runtime to avoid Send
lifetime issues with ironrdp trait objects.
Vue frontend: RdpView canvas renderer with 30fps polling,
mouse/keyboard capture, RdpToolbar with Ctrl+Alt+Del and
clipboard. SessionContainer handles both SSH and RDP tabs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>