feat: debug logging macro + MCP tools inject button in copilot
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 2m58s

Debug logging:
- wraith_log!() macro available in all modules, writes to wraith.log
- SSH connect/auth, PTY spawn, RDP connect all log with session IDs
- MCP startup panic now shows the actual error message

Copilot "Tools" button:
- Shows when a PTY session is active in the copilot panel
- Injects a formatted list of all 18 MCP tools into the chat
- Groups tools by category: session, terminal, SFTP, network, utilities
- Includes parameter signatures so the AI knows how to call them

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-25 11:12:23 -04:00
parent 5d1aeb5fe3
commit 357491b4e8
5 changed files with 60 additions and 0 deletions

View File

@ -1,3 +1,12 @@
// Global debug log macro — must be declared before modules that use it
#[macro_export]
macro_rules! wraith_log {
($($arg:tt)*) => {{
let msg = format!($($arg)*);
let _ = $crate::write_log(&$crate::data_directory().join("wraith.log"), &msg);
}};
}
pub mod db;
pub mod vault;
pub mod settings;

View File

@ -89,6 +89,7 @@ impl PtyService {
scrollback: &ScrollbackRegistry,
) -> Result<String, String> {
let session_id = uuid::Uuid::new_v4().to_string();
wraith_log!("[PTY] Spawning shell: {} (session {})", shell_path, session_id);
let pty_system = native_pty_system();
let pair = pty_system

View File

@ -88,6 +88,7 @@ impl RdpService {
pub fn connect(&self, config: RdpConfig) -> Result<String, String> {
let session_id = uuid::Uuid::new_v4().to_string();
wraith_log!("[RDP] Connecting to {}:{} as {} (session {})", config.hostname, config.port, config.username, session_id);
let width = config.width;
let height = config.height;
let hostname = config.hostname.clone();

View File

@ -87,6 +87,7 @@ impl SshService {
pub async fn connect(&self, app_handle: AppHandle, hostname: &str, port: u16, username: &str, auth: AuthMethod, cols: u32, rows: u32, sftp_service: &SftpService, scrollback: &ScrollbackRegistry, error_watcher: &ErrorWatcher) -> Result<String, String> {
let session_id = uuid::Uuid::new_v4().to_string();
wraith_log!("[SSH] Connecting to {}:{} as {} (session {})", hostname, port, username, session_id);
let config = Arc::new(russh::client::Config::default());
let handler = SshClient { host_key_store: HostKeyStore::new(self.db.clone()), hostname: hostname.to_string(), port };
@ -150,6 +151,8 @@ impl SshService {
}
}
wraith_log!("[SSH] Connected and authenticated: {}", session_id);
// Create scrollback buffer for MCP terminal_read
let scrollback_buf = scrollback.create(&session_id);
error_watcher.watch(&session_id);

View File

@ -37,6 +37,14 @@
>
Kill
</button>
<button
v-if="connected"
class="px-2 py-0.5 text-[10px] rounded border border-[var(--wraith-border)] text-[var(--wraith-text-muted)] hover:text-[var(--wraith-text-primary)] cursor-pointer"
title="Inject available MCP tools into the chat"
@click="injectTools"
>
Tools
</button>
</div>
</div>
@ -226,6 +234,44 @@ async function launch(): Promise<void> {
}
}
function injectTools(): void {
if (!sessionId || !connected.value) return;
const toolsPrompt = [
"You have access to these Wraith MCP tools via the wraith-mcp-bridge:",
"",
"SESSION MANAGEMENT:",
" list_sessions — List all active SSH/RDP/PTY sessions",
"",
"TERMINAL:",
" terminal_read(session_id, lines?) — Read recent terminal output (ANSI stripped)",
" terminal_execute(session_id, command, timeout_ms?) — Run a command and capture output",
" terminal_screenshot(session_id) — Capture RDP session as PNG",
"",
"SFTP:",
" sftp_list(session_id, path) — List remote directory",
" sftp_read(session_id, path) — Read remote file",
" sftp_write(session_id, path, content) — Write remote file",
"",
"NETWORK:",
" network_scan(session_id, subnet) — Discover devices on subnet (ARP + ping sweep)",
" port_scan(session_id, target, ports?) — Scan TCP ports",
" ping(session_id, target) — Ping a host",
" traceroute(session_id, target) — Traceroute to host",
" dns_lookup(session_id, domain, record_type?) — DNS lookup",
" whois(session_id, target) — Whois lookup",
" wake_on_lan(session_id, mac_address) — Send WoL magic packet",
" bandwidth_test(session_id) — Internet speed test",
"",
"UTILITIES (no session needed):",
" subnet_calc(cidr) — Calculate subnet details",
" generate_ssh_key(key_type, comment?) — Generate SSH key pair",
" generate_password(length?, uppercase?, lowercase?, digits?, symbols?) — Generate password",
"",
].join("\n");
invoke("pty_write", { sessionId, data: toolsPrompt + "\r" }).catch(() => {});
}
function kill(): void {
if (sessionId) {
invoke("disconnect_pty", { sessionId }).catch(() => {});