Link support codes to agent sessions

- Server: Accept support_code param in WebSocket connection
- Server: Link code to session when agent connects, mark as connected
- Server: Mark code as completed when agent disconnects
- Agent: Accept support code from command line argument
- Agent: Send hostname and support_code in WebSocket params
- Portal: Trigger agent download with code in filename
- Portal: Show code reminder in download instructions
- Dashboard: Add machines list fetching (Access tab)
- Add TODO.md for feature tracking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-28 14:11:52 -07:00
parent 9af59158b2
commit 1d2ca47771
10 changed files with 447 additions and 21 deletions

View File

@@ -24,6 +24,10 @@ pub struct AgentParams {
agent_id: String,
#[serde(default)]
agent_name: Option<String>,
#[serde(default)]
support_code: Option<String>,
#[serde(default)]
hostname: Option<String>,
}
#[derive(Debug, Deserialize)]
@@ -38,10 +42,12 @@ pub async fn agent_ws_handler(
Query(params): Query<AgentParams>,
) -> impl IntoResponse {
let agent_id = params.agent_id;
let agent_name = params.agent_name.unwrap_or_else(|| agent_id.clone());
let agent_name = params.hostname.or(params.agent_name).unwrap_or_else(|| agent_id.clone());
let support_code = params.support_code;
let sessions = state.sessions.clone();
let support_codes = state.support_codes.clone();
ws.on_upgrade(move |socket| handle_agent_connection(socket, sessions, agent_id, agent_name))
ws.on_upgrade(move |socket| handle_agent_connection(socket, sessions, support_codes, agent_id, agent_name, support_code))
}
/// WebSocket handler for viewer connections
@@ -60,8 +66,10 @@ pub async fn viewer_ws_handler(
async fn handle_agent_connection(
socket: WebSocket,
sessions: SessionManager,
support_codes: crate::support_codes::SupportCodeManager,
agent_id: String,
agent_name: String,
support_code: Option<String>,
) {
info!("Agent connected: {} ({})", agent_name, agent_id);
@@ -70,6 +78,13 @@ async fn handle_agent_connection(
info!("Session created: {}", session_id);
// If a support code was provided, mark it as connected
if let Some(ref code) = support_code {
info!("Linking support code {} to session {}", code, session_id);
support_codes.mark_connected(code, Some(agent_name.clone()), Some(agent_id.clone())).await;
support_codes.link_session(code, session_id).await;
}
let (mut ws_sender, mut ws_receiver) = socket.split();
// Task to forward input events from viewers to agent
@@ -82,6 +97,8 @@ async fn handle_agent_connection(
});
let sessions_cleanup = sessions.clone();
let support_codes_cleanup = support_codes.clone();
let support_code_cleanup = support_code.clone();
// Main loop: receive frames from agent and broadcast to viewers
while let Some(msg) = ws_receiver.next().await {
@@ -119,6 +136,13 @@ async fn handle_agent_connection(
// Cleanup
input_forward.abort();
sessions_cleanup.remove_session(session_id).await;
// Mark support code as completed if one was used
if let Some(ref code) = support_code_cleanup {
support_codes_cleanup.mark_completed(code).await;
info!("Support code {} marked as completed", code);
}
info!("Session {} ended", session_id);
}

View File

@@ -147,6 +147,27 @@ impl SupportCodeManager {
}
}
/// Link a support code to an actual WebSocket session
pub async fn link_session(&self, code: &str, real_session_id: Uuid) {
let mut codes = self.codes.write().await;
if let Some(support_code) = codes.get_mut(code) {
// Update session_to_code mapping with real session ID
let old_session_id = support_code.session_id;
support_code.session_id = real_session_id;
// Update the reverse mapping
let mut session_to_code = self.session_to_code.write().await;
session_to_code.remove(&old_session_id);
session_to_code.insert(real_session_id, code.to_string());
}
}
/// Get code by its code string
pub async fn get_code(&self, code: &str) -> Option<SupportCode> {
let codes = self.codes.read().await;
codes.get(code).cloned()
}
/// Mark a code as completed
pub async fn mark_completed(&self, code: &str) {
let mut codes = self.codes.write().await;