wraith/frontend/src/composables/useSftp.ts
Vantz Stockwell 8572e6e7ea fix: wire SSH/SFTP/terminal to real Go backend — kill all stubs
Critical path wired end-to-end:
- ConnectSSH on WraithApp resolves credentials from vault, builds auth methods
- SSH output handler emits Wails events (base64) to frontend
- useTerminal.ts forwards keystrokes to SSHService.Write, resize to SSHService.Resize
- useTerminal.ts listens for ssh:data:{sessionId} events and writes to xterm.js
- session.store.ts connect() calls real Go ConnectSSH, not mock
- useSftp.ts calls real SFTPService.List instead of hardcoded mock data
- SFTP client auto-registered on SSH connection via pkg/sftp
- DisconnectSession cleans up both SSH and SFTP

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:49:21 -04:00

82 lines
1.9 KiB
TypeScript

import { ref, type Ref } from "vue";
import { Call } from "@wailsio/runtime";
const SFTP = "github.com/vstockwell/wraith/internal/sftp.SFTPService";
export interface FileEntry {
name: string;
path: string;
size: number;
isDir: boolean;
permissions: string;
modTime: string;
}
export interface UseSftpReturn {
currentPath: Ref<string>;
entries: Ref<FileEntry[]>;
isLoading: Ref<boolean>;
followTerminal: Ref<boolean>;
navigateTo: (path: string) => Promise<void>;
goUp: () => Promise<void>;
refresh: () => Promise<void>;
}
/**
* Composable that manages SFTP file browsing state.
* Calls the real Go SFTPService via Wails bindings.
*/
export function useSftp(sessionId: string): UseSftpReturn {
const currentPath = ref("/");
const entries = ref<FileEntry[]>([]);
const isLoading = ref(false);
const followTerminal = ref(false);
async function listDirectory(path: string): Promise<FileEntry[]> {
try {
const result = await Call.ByName(`${SFTP}.List`, sessionId, path) as FileEntry[];
return result ?? [];
} catch (err) {
console.error("SFTP list error:", err);
return [];
}
}
async function navigateTo(path: string): Promise<void> {
isLoading.value = true;
try {
currentPath.value = path;
entries.value = await listDirectory(path);
} finally {
isLoading.value = false;
}
}
async function goUp(): Promise<void> {
const parts = currentPath.value.split("/").filter(Boolean);
if (parts.length <= 1) {
await navigateTo("/");
return;
}
parts.pop();
await navigateTo("/" + parts.join("/"));
}
async function refresh(): Promise<void> {
await navigateTo(currentPath.value);
}
// Load home directory on init
navigateTo("/home");
return {
currentPath,
entries,
isLoading,
followTerminal,
navigateTo,
goUp,
refresh,
};
}