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>
82 lines
1.9 KiB
TypeScript
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,
|
|
};
|
|
}
|