diff --git a/src-tauri/src/commands/kube.rs b/src-tauri/src/commands/kube.rs index c48bae11..7d703092 100644 --- a/src-tauri/src/commands/kube.rs +++ b/src-tauri/src/commands/kube.rs @@ -6268,7 +6268,10 @@ fn parse_crds_json(json_str: &str) -> Result, String> { .filter_map(|ver| { let version_name = ver.get("name").and_then(|n| n.as_str())?.to_string(); let served = ver.get("served").and_then(|s| s.as_bool()).unwrap_or(true); - let storage = ver.get("storage").and_then(|s| s.as_bool()).unwrap_or(false); + let storage = ver + .get("storage") + .and_then(|s| s.as_bool()) + .unwrap_or(false); // Parse printer columns for this version let printer_columns: Vec = ver @@ -6277,11 +6280,26 @@ fn parse_crds_json(json_str: &str) -> Result, String> { .map(|cols| { cols.iter() .filter_map(|col| { - let col_name = col.get("name").and_then(|n| n.as_str())?.to_string(); - let json_path = col.get("jsonPath").and_then(|j| j.as_str())?.to_string(); - let column_type = col.get("type").and_then(|t| t.as_str()).unwrap_or("string").to_string(); - let description = col.get("description").and_then(|d| d.as_str()).map(|s| s.to_string()); - let priority = col.get("priority").and_then(|p| p.as_i64()).unwrap_or(0) as i32; + let col_name = + col.get("name").and_then(|n| n.as_str())?.to_string(); + let json_path = col + .get("jsonPath") + .and_then(|j| j.as_str())? + .to_string(); + let column_type = col + .get("type") + .and_then(|t| t.as_str()) + .unwrap_or("string") + .to_string(); + let description = col + .get("description") + .and_then(|d| d.as_str()) + .map(|s| s.to_string()); + let priority = col + .get("priority") + .and_then(|p| p.as_i64()) + .unwrap_or(0) + as i32; Some(PrinterColumn { name: col_name, diff --git a/src-tauri/src/commands/shell.rs b/src-tauri/src/commands/shell.rs index 8ca63e67..6509023f 100644 --- a/src-tauri/src/commands/shell.rs +++ b/src-tauri/src/commands/shell.rs @@ -286,16 +286,15 @@ pub async fn start_pty_exec_session( .prepare("SELECT encrypted_content FROM kubeconfig_files WHERE is_active = 1 LIMIT 1") .map_err(|e| format!("Failed to query active kubeconfig: {e}"))?; - let encrypted: Option = stmt - .query_row([], |row| row.get(0)) - .ok(); + let encrypted: Option = stmt.query_row([], |row| row.get(0)).ok(); if let Some(enc) = encrypted { let content = crate::integrations::auth::decrypt_token(&enc) .map_err(|e| format!("Failed to decrypt kubeconfig: {e}"))?; // Write to temp file - let temp_path = std::env::temp_dir().join(format!("kubeconfig-{}.yaml", uuid::Uuid::now_v7())); + let temp_path = + std::env::temp_dir().join(format!("kubeconfig-{}.yaml", uuid::Uuid::now_v7())); std::fs::write(&temp_path, content) .map_err(|e| format!("Failed to write kubeconfig: {e}"))?; @@ -306,8 +305,8 @@ pub async fn start_pty_exec_session( }; // Locate kubectl - let kubectl_path = crate::shell::kubectl::locate_kubectl() - .map_err(|e| format!("kubectl not found: {e}"))?; + let kubectl_path = + crate::shell::kubectl::locate_kubectl().map_err(|e| format!("kubectl not found: {e}"))?; // Start session let params = crate::shell::session::SessionParams { @@ -345,16 +344,15 @@ pub async fn start_pty_attach_session( .prepare("SELECT encrypted_content FROM kubeconfig_files WHERE is_active = 1 LIMIT 1") .map_err(|e| format!("Failed to query active kubeconfig: {e}"))?; - let encrypted: Option = stmt - .query_row([], |row| row.get(0)) - .ok(); + let encrypted: Option = stmt.query_row([], |row| row.get(0)).ok(); if let Some(enc) = encrypted { let content = crate::integrations::auth::decrypt_token(&enc) .map_err(|e| format!("Failed to decrypt kubeconfig: {e}"))?; // Write to temp file - let temp_path = std::env::temp_dir().join(format!("kubeconfig-{}.yaml", uuid::Uuid::now_v7())); + let temp_path = + std::env::temp_dir().join(format!("kubeconfig-{}.yaml", uuid::Uuid::now_v7())); std::fs::write(&temp_path, content) .map_err(|e| format!("Failed to write kubeconfig: {e}"))?; @@ -365,8 +363,8 @@ pub async fn start_pty_attach_session( }; // Locate kubectl - let kubectl_path = crate::shell::kubectl::locate_kubectl() - .map_err(|e| format!("kubectl not found: {e}"))?; + let kubectl_path = + crate::shell::kubectl::locate_kubectl().map_err(|e| format!("kubectl not found: {e}"))?; // Start session let params = crate::shell::session::SessionParams { diff --git a/src-tauri/src/shell/pty.rs b/src-tauri/src/shell/pty.rs index 90aee22a..dbc1b9d8 100644 --- a/src-tauri/src/shell/pty.rs +++ b/src-tauri/src/shell/pty.rs @@ -50,12 +50,13 @@ impl PtySession { .spawn_command(cmd) .context("Failed to spawn command in PTY")?; - debug!("PTY session spawned: {} (PID: {:?})", command, child.process_id()); + debug!( + "PTY session spawned: {} (PID: {:?})", + command, + child.process_id() + ); - Ok(Self { - pair, - child, - }) + Ok(Self { pair, child }) } /// Spawn kubectl exec session @@ -126,7 +127,11 @@ impl PtySession { /// Write data to PTY stdin pub fn write(&mut self, data: &[u8]) -> Result<()> { - let mut writer = self.pair.master.take_writer().context("PTY writer unavailable")?; + let mut writer = self + .pair + .master + .take_writer() + .context("PTY writer unavailable")?; writer .write_all(data) .context("Failed to write to PTY stdin")?; @@ -136,7 +141,11 @@ impl PtySession { /// Read available data from PTY stdout/stderr (non-blocking) pub fn read(&mut self) -> Result> { - let mut reader = self.pair.master.try_clone_reader().context("PTY reader unavailable")?; + let mut reader = self + .pair + .master + .try_clone_reader() + .context("PTY reader unavailable")?; let mut buffer = vec![0u8; 4096]; // Non-blocking read with timeout @@ -175,12 +184,16 @@ impl PtySession { /// Kill the child process pub fn kill(&mut self) -> Result<()> { - self.child.kill().context("Failed to kill PTY child process") + self.child + .kill() + .context("Failed to kill PTY child process") } /// Wait for the child process to exit pub fn wait(&mut self) -> Result { - self.child.wait().context("Failed to wait for PTY child process") + self.child + .wait() + .context("Failed to wait for PTY child process") } } @@ -214,8 +227,10 @@ mod tests { let output_str = String::from_utf8_lossy(&output); // Should contain "hello" - assert!(output_str.contains("hello") || output_str.is_empty(), - "Expected output to contain 'hello' or be empty (timing issue)"); + assert!( + output_str.contains("hello") || output_str.is_empty(), + "Expected output to contain 'hello' or be empty (timing issue)" + ); } #[test] @@ -241,8 +256,10 @@ mod tests { // Output should contain our test data (cat echoes it back) let output_str = String::from_utf8_lossy(&output); - assert!(output_str.contains("test input") || output_str.is_empty(), - "Expected output to contain 'test input' or be empty (timing issue)"); + assert!( + output_str.contains("test input") || output_str.is_empty(), + "Expected output to contain 'test input' or be empty (timing issue)" + ); } #[test] @@ -303,7 +320,9 @@ mod tests { let output_str = String::from_utf8_lossy(&output); // Should contain our test value - assert!(output_str.contains("test_value") || output_str.is_empty(), - "Expected output to contain 'test_value' or be empty (timing issue)"); + assert!( + output_str.contains("test_value") || output_str.is_empty(), + "Expected output to contain 'test_value' or be empty (timing issue)" + ); } } diff --git a/src-tauri/src/shell/session.rs b/src-tauri/src/shell/session.rs index 64001f8c..ae4aeda5 100644 --- a/src-tauri/src/shell/session.rs +++ b/src-tauri/src/shell/session.rs @@ -263,9 +263,7 @@ impl SessionManager { /// Send stdin data to a session pub async fn send_stdin(&self, session_id: &str, data: Vec) -> Result<()> { let sessions = self.sessions.read().await; - let session = sessions - .get(session_id) - .context("Session not found")?; + let session = sessions.get(session_id).context("Session not found")?; session .stdin_tx @@ -278,9 +276,7 @@ impl SessionManager { /// Resize a session's PTY pub async fn resize_session(&self, session_id: &str, rows: u16, cols: u16) -> Result<()> { let sessions = self.sessions.read().await; - let session = sessions - .get(session_id) - .context("Session not found")?; + let session = sessions.get(session_id).context("Session not found")?; session .control_tx @@ -293,9 +289,7 @@ impl SessionManager { /// Terminate a session pub async fn terminate_session(&self, session_id: &str) -> Result<()> { let sessions = self.sessions.read().await; - let session = sessions - .get(session_id) - .context("Session not found")?; + let session = sessions.get(session_id).context("Session not found")?; session .control_tx diff --git a/src/lib/tauriCommands.ts b/src/lib/tauriCommands.ts index 58657bc5..0e80aa76 100644 --- a/src/lib/tauriCommands.ts +++ b/src/lib/tauriCommands.ts @@ -1513,3 +1513,53 @@ export const listCrdsCmd = (clusterId: string) => export const listCustomResourcesCmd = (clusterId: string, group: string, version: string, resource: string, namespace: string) => invoke("list_custom_resources", { clusterId, group, version, resource, namespace }); + +// ─── PTY Terminal Commands ──────────────────────────────────────────────────── + +export interface PtySessionInfo { + session_id: string; + cluster_id: string; + namespace: string; + pod_name: string; + container_name: string | null; + session_type: "exec" | "attach"; +} + +export const startPtyExecSessionCmd = ( + clusterId: string, + namespace: string, + podName: string, + containerName: string | null, + shell: string +) => + invoke("start_pty_exec_session", { + clusterId, + namespace, + podName, + containerName, + shell, + }); + +export const startPtyAttachSessionCmd = ( + clusterId: string, + namespace: string, + podName: string, + containerName: string | null +) => + invoke("start_pty_attach_session", { + clusterId, + namespace, + podName, + containerName, + }); + +export const sendPtyStdinCmd = (sessionId: string, data: string) => + invoke("send_pty_stdin", { sessionId, data }); + +export const resizePtySessionCmd = (sessionId: string, rows: number, cols: number) => + invoke("resize_pty_session", { sessionId, rows, cols }); + +export const terminatePtySessionCmd = (sessionId: string) => + invoke("terminate_pty_session", { sessionId }); + +export const listPtySessionsCmd = () => invoke("list_pty_sessions", {});