feat: wire SFTP CWD following — listen for OSC 7 events + inject shell hook
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m2s
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m2s
Two missing halves of CWD tracking:
1. Frontend: useSftp now listens for ssh:cwd:{sessionId} Wails events
and calls navigateTo() when followTerminal is enabled (default: on).
2. Backend: re-added shell integration injection with stty -echo to
suppress visible command output. Leading space keeps it out of
shell history. Handles both bash (PROMPT_COMMAND) and zsh (precmd).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c953659852
commit
bce77e0932
@ -1,5 +1,5 @@
|
|||||||
import { ref, type Ref } from "vue";
|
import { ref, onBeforeUnmount, type Ref } from "vue";
|
||||||
import { Call } from "@wailsio/runtime";
|
import { Call, Events } from "@wailsio/runtime";
|
||||||
|
|
||||||
const SFTP = "github.com/vstockwell/wraith/internal/sftp.SFTPService";
|
const SFTP = "github.com/vstockwell/wraith/internal/sftp.SFTPService";
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export function useSftp(sessionId: string): UseSftpReturn {
|
|||||||
const currentPath = ref("/");
|
const currentPath = ref("/");
|
||||||
const entries = ref<FileEntry[]>([]);
|
const entries = ref<FileEntry[]>([]);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const followTerminal = ref(false);
|
const followTerminal = ref(true);
|
||||||
|
|
||||||
async function listDirectory(path: string): Promise<FileEntry[]> {
|
async function listDirectory(path: string): Promise<FileEntry[]> {
|
||||||
try {
|
try {
|
||||||
@ -66,6 +66,28 @@ export function useSftp(sessionId: string): UseSftpReturn {
|
|||||||
await navigateTo(currentPath.value);
|
await navigateTo(currentPath.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Listen for CWD changes from the Go backend (OSC 7 tracking)
|
||||||
|
const cleanupCwd = Events.On(`ssh:cwd:${sessionId}`, (event: any) => {
|
||||||
|
if (!followTerminal.value) return;
|
||||||
|
let newPath: string;
|
||||||
|
if (typeof event === "string") {
|
||||||
|
newPath = event;
|
||||||
|
} else if (event?.data && typeof event.data === "string") {
|
||||||
|
newPath = event.data;
|
||||||
|
} else if (Array.isArray(event?.data)) {
|
||||||
|
newPath = String(event.data[0] ?? "");
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newPath && newPath !== currentPath.value) {
|
||||||
|
navigateTo(newPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (cleanupCwd) cleanupCwd();
|
||||||
|
});
|
||||||
|
|
||||||
// Load home directory on init
|
// Load home directory on init
|
||||||
navigateTo("/home");
|
navigateTo("/home");
|
||||||
|
|
||||||
|
|||||||
@ -142,10 +142,23 @@ func (s *SSHService) Connect(hostname string, port int, username string, authMet
|
|||||||
// Launch goroutine to read stdout and forward data via the output handler
|
// Launch goroutine to read stdout and forward data via the output handler
|
||||||
go s.readLoop(sessionID, stdout)
|
go s.readLoop(sessionID, stdout)
|
||||||
|
|
||||||
// CWD tracking via OSC 7 is handled passively — the CWDTracker in readLoop
|
// Inject shell integration for CWD tracking (OSC 7).
|
||||||
// parses OSC 7 sequences if the remote shell already emits them. Automatic
|
// Uses stty -echo to suppress the command from appearing in the terminal,
|
||||||
// PROMPT_COMMAND injection is deferred until we have a non-echoing mechanism
|
// then restores echo. A leading space keeps it out of shell history.
|
||||||
// (e.g., writing to a second SSH channel or modifying .bashrc).
|
go func() {
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
// Suppress echo, set PROMPT_COMMAND for bash (zsh uses precmd),
|
||||||
|
// restore echo, then clear the current line so no visual artifact remains.
|
||||||
|
injection := " stty -echo 2>/dev/null; " +
|
||||||
|
ShellIntegrationCommand("bash") + "; " +
|
||||||
|
"if [ -n \"$ZSH_VERSION\" ]; then " + ShellIntegrationCommand("zsh") + "; fi; " +
|
||||||
|
"stty echo 2>/dev/null\n"
|
||||||
|
sshSession.mu.Lock()
|
||||||
|
if sshSession.Stdin != nil {
|
||||||
|
_, _ = sshSession.Stdin.Write([]byte(injection))
|
||||||
|
}
|
||||||
|
sshSession.mu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
return sessionID, nil
|
return sessionID, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user