feat(fm): remote View/Edit — native editor (daemon) + working-copy save-back (classic SSH)#33
Merged
Merged
Conversation
The SFTP file manager (F3/F4) now opens a file on a remote host in the
native code editor when that host has a live persistent (daemon)
session: the editor buffer syncs with the daemon and writes back
safely — real Save, not a throwaway download. This is the devhost /
resilient-host case the user actually works in.
The file manager is keyed by the SSH `node_id`; the native editor needs
a daemon `HostId`, which is opaque and only known after the daemon
handshake completes. Bridge it pull-based: record `node_id → daemon
SessionId` when a resilient host connects (`try_open_daemon_ssh_terminal`)
or is adopted (`adopt_daemon_session`), and resolve a live `HostId` on
demand via `RemoteServerManager::{host_id_for_session, client_for_host}`
at open time (`daemon_host_for_node`). Re-checking the live manager on
every lookup makes a stale entry harmless (session gone → graceful
fallback), and since `SessionId`s are never reused an entry can only
ever resolve to its own host or to nothing — never a wrong host. The
daemon session/connect flow itself is untouched; the mapping is
observe-only, so no blast radius on the persistence feature.
Classic SSH-only hosts (no daemon) fall back to an honest toast instead
of editing a copy that would never save back; SFTP working-copy
save-back over classic SSH is the next increment.
Verified: `cargo check -p warp` compiles clean (build.rs enables
`local_tty` + `local_fs` = the shipping feature set; the native path is
actually compiled, not cfg'd out). Runtime acceptance: F3/F4 on a file
in a file-manager pane on a devhost (daemon) tab round-trips edit+save.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…k (F3/F4) Completes remote View/Edit for the second host type: a classic SSH host with no persistent daemon. F3/F4 on such a host now downloads the file to a local working copy, opens it in the native editor, and uploads it back over SFTP on every save — real Save, end to end. Mechanism (additive, low blast radius — one new subscription, no editor surgery): - `FileManagerRegistry::backend_for_namespace` hands the workspace a live SFTP connection already open for the host, so there is no second connect. - `begin_remote_sftp_edit` downloads into a unique local working copy (original filename kept for language detection + tab title), opens it via `add_tab_for_code_file`, and records it in `Workspace::remote_sftp_edits` keyed by the canonical local path — holding its own backend `Arc` so save-back survives the file-manager pane closing. - A single subscription to `GlobalBufferModelEvent::FileSaved` drives `on_remote_working_copy_saved`: if the saved path is a tracked working copy, upload it back. Ordinary local saves return immediately. A save that lands mid-upload is coalesced (`resave_pending`) and re-run, so none is dropped. No-data-loss contract: the remote file is only ever written by a save-back upload (never on open); a failed upload keeps the working copy and shows a prominent, persistent error — the editor already flagged the local save as done, so a silent upload failure would be dangerous. Concurrent external edits are last-write-wins on the next save, inherent to editing over plain SFTP; daemon hosts get the version-checked native path instead. Working copies live under the OS temp dir in unique per-edit dirs (pid + counter, never reused); small text files, OS-reclaimed. There is no buffer-closed event to hook for eager deletion, and stale map entries are harmless: a closed buffer emits no further save event, so nothing uploads. Verified: `cargo check -p warp` clean (0 errors/warnings); `fm_registry` unit tests incl. new `backend_for_namespace` pass. Runtime acceptance: F3/F4 on a file in a file-manager pane connected to a non-daemon SSH host, edit + Save, confirm the change lands on the host. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Completes remote View/Edit (F3/F4) in the MC-style file manager for both remote host types. Local View/Edit shipped in #32; this PR adds the two remote paths:
This closes the last mandatory gap in the file-manager Pflichtliste.
Host type 1 — daemon host (native)
The file manager is keyed by SSH
node_id; the native editor needs a daemonHostId, which is opaque and only known after the daemon handshake. Bridged pull-based, observe-only:node_id → daemon SessionIdon connect (try_open_daemon_ssh_terminal) / adopt (adopt_daemon_session).HostIdon demand (daemon_host_for_node) viaRemoteServerManager::{host_id_for_session, client_for_host}.Self-correcting (session gone → graceful fallback) and never wrong (SessionIds are monotonic, never reused). The daemon session/connect flow is untouched — zero blast radius on the persistence feature.
Host type 2 — classic SSH (working copy + save-back)
Additive, low blast radius — one new event subscription, no editor surgery:
FileManagerRegistry::backend_for_namespacelends the workspace a live SFTP connection already open for the host (no second connect).begin_remote_sftp_editdownloads the file into a unique local working copy (original filename kept for language + tab title), opens it viaadd_tab_for_code_file, and records it inWorkspace::remote_sftp_editskeyed by the canonical local path — holding its own backendArcso save-back survives the file-manager pane closing.GlobalBufferModelEvent::FileSaveddriveson_remote_working_copy_saved: if the saved path is a tracked working copy, upload it back. Ordinary local saves return immediately. A save landing mid-upload is coalesced (resave_pending) and re-run, so none is dropped.No-data-loss contract
Working copies live under the OS temp dir in unique per-edit dirs (pid + counter, never reused; small text files, OS-reclaimed). No buffer-closed event exists to hook for eager deletion; stale map entries are harmless (a closed buffer emits no further save event → nothing uploads).
Verification
cargo check -p warp— clean, 0 errors/warnings.build.rsenableslocal_tty+local_fson non-wasm, so both new#[cfg(all(unix, feature = "local_tty"))]paths are actually compiled (the shipping feature set). The PR gate (cargo check --bin zaplex) exercises the same cfgs.cargo test -p warp --lib sftp_manager::fm_registry— 6 passed (incl. newbackend_for_namespace); the whole lib compiles clean in test cfg too.Files
app/src/workspace/view.rs—daemon_node_sessions+daemon_host_for_node(native path);remote_sftp_edits+RemoteSftpEdit+begin_remote_sftp_edit+on_remote_working_copy_saved+FileSavedsubscription (classic-SSH path);OpenFileInEditorroutes through both.app/src/sftp_manager/fm_registry.rs—backend_for_namespace(+ unit test).🤖 Generated with Claude Code