Add cancellation flow for support sessions
Server changes: - Allow cancelling connected codes (not just pending) - Reject agent connections with cancelled codes - Periodic cancellation check during active sessions - Send Disconnect message when code is cancelled Agent changes: - Detect cancellation via Disconnect message - Show Windows MessageBox to notify user - Exit cleanly without reconnecting for support sessions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,11 @@ use anyhow::Result;
|
||||
use tracing::{info, error, Level};
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
#[cfg(windows)]
|
||||
use windows::Win32::UI::WindowsAndMessaging::{MessageBoxW, MB_OK, MB_ICONINFORMATION};
|
||||
#[cfg(windows)]
|
||||
use windows::core::PCWSTR;
|
||||
|
||||
/// Extract a 6-digit support code from the executable's filename.
|
||||
/// Looks for patterns like "GuruConnect-123456.exe" or "123456.exe"
|
||||
fn extract_code_from_filename() -> Option<String> {
|
||||
@@ -50,6 +55,37 @@ fn extract_code_from_filename() -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Show a message box to the user (Windows only)
|
||||
#[cfg(windows)]
|
||||
fn show_message_box(title: &str, message: &str) {
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
|
||||
// Convert strings to wide strings for Windows API
|
||||
let title_wide: Vec<u16> = OsStr::new(title)
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
let message_wide: Vec<u16> = OsStr::new(message)
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
|
||||
unsafe {
|
||||
MessageBoxW(
|
||||
None,
|
||||
PCWSTR(message_wide.as_ptr()),
|
||||
PCWSTR(title_wide.as_ptr()),
|
||||
MB_OK | MB_ICONINFORMATION,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn show_message_box(_title: &str, _message: &str) {
|
||||
// No-op on non-Windows platforms
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Initialize logging
|
||||
@@ -85,6 +121,7 @@ async fn main() -> Result<()> {
|
||||
async fn run_agent(config: config::Config) -> Result<()> {
|
||||
// Create session manager
|
||||
let mut session = session::SessionManager::new(config.clone());
|
||||
let is_support_session = config.support_code.is_some();
|
||||
|
||||
// Connect to server and run main loop
|
||||
loop {
|
||||
@@ -96,15 +133,47 @@ async fn run_agent(config: config::Config) -> Result<()> {
|
||||
|
||||
// Run session until disconnect
|
||||
if let Err(e) = session.run().await {
|
||||
let error_msg = e.to_string();
|
||||
|
||||
// Check if this is a cancellation
|
||||
if error_msg.contains("SESSION_CANCELLED") {
|
||||
info!("Session was cancelled by technician");
|
||||
show_message_box(
|
||||
"Support Session Ended",
|
||||
"The support session was cancelled by the technician.\n\nThis window will close automatically.",
|
||||
);
|
||||
// Exit cleanly without reconnecting
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
error!("Session error: {}", e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let error_msg = e.to_string();
|
||||
|
||||
// Check if connection was rejected due to cancelled code
|
||||
if error_msg.contains("cancelled") {
|
||||
info!("Support code was cancelled before connection");
|
||||
show_message_box(
|
||||
"Support Session Cancelled",
|
||||
"This support session has been cancelled.\n\nPlease contact your technician for a new support code.",
|
||||
);
|
||||
// Exit cleanly without reconnecting
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
error!("Connection failed: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait before reconnecting
|
||||
// For support sessions, don't reconnect if something goes wrong
|
||||
if is_support_session {
|
||||
info!("Support session ended, not reconnecting");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Wait before reconnecting (only for persistent agent connections)
|
||||
info!("Reconnecting in 5 seconds...");
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
||||
}
|
||||
|
||||
@@ -202,6 +202,10 @@ impl SessionManager {
|
||||
|
||||
Some(message::Payload::Disconnect(disc)) => {
|
||||
tracing::info!("Disconnect requested: {}", disc.reason);
|
||||
// Check if this is a cancellation
|
||||
if disc.reason.contains("cancelled") {
|
||||
return Err(anyhow::anyhow!("SESSION_CANCELLED: {}", disc.reason));
|
||||
}
|
||||
return Err(anyhow::anyhow!("Disconnect: {}", disc.reason));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user