VAPP-7: Per-device file browser tab #34

Merged
nrupard merged 2 commits from feat/file-browser-tab-vapp-7 into main 2026-06-12 19:43:06 +02:00
Owner

What

Implements VAPP-7: enables the Files tab on the device detail page (previously an inert DisabledTab) as a live file browser. Reachable for online devices alongside the Terminal and Desktop tabs.

How

The prior run parked this because it assumed a control-channel getdir relay and a cross-origin .ashx download path, neither of which existed. Both dependencies have since landed: VA-3 (agent Files tunnel mode) and VS-3 (server file-tunnel allocator) are resolved, and the relay rendezvous is usage-agnostic (verified in vervain-server meshrelay.rs). So the Files tab rides the agent's Files tunnel (usage=4) exactly like Terminal (usage=1) and Desktop (usage=2): the new open_files_tunnel server fn reuses the shared open_relay_tunnel(device_id, usage) helper, the browser connects DIRECTLY to vervain-server's relay, and drives the agent's run_files protocol over the rendezvous WebSocket. No control-channel relay and no streaming Axum proxy are needed.

Protocol implemented exactly from vervain-agent::host::tunnel::run_files:

  • ls directory listing (name / type / size / mtime, folders first)
  • mkdir, rename, rm (delete with a confirm dialog)
  • download: streams to disk via the File System Access API (showSaveFilePicker + a streaming WritableStream), writing one 16 KiB chunk at a time and acking per chunk for backpressure, so a >100 MB file is never held whole in memory. Browsers without the API fall back to a buffered Blob download. The File System Access calls go through js_sys::Reflect, so no web_sys_unstable_apis build flag is required.
  • upload: reads the local file slice-by-slice and is flow-controlled by the agent's per-chunk uploadack, with a progress indicator. Chunks are 0x00-escaped per the agent's JSON-disambiguation rule.

UI

Breadcrumb + Up navigation, folders-first listing with size and modified columns, New folder / Upload toolbar actions, and per-row Download / Rename / Delete. Offline devices and tunnel-open rights errors surface via the routing-ack mapping and a LoadError banner; the tab is disabled when the device is offline.

Acceptance criteria

  • Files tab lists directories with navigation, sizes, and modified times
  • File download works for files >100 MB without loading the whole file into memory (File System Access API streams 16 KiB chunks straight to disk)
  • Upload with progress indicator
  • mkdir / rename / delete with confirmation on delete
  • Rights and offline errors surface in-pane
  • Linked as depends-on to VA-3 and VS-3 (already linked in YouTrack)

Checks

just check equivalents all green in the rust-builder-glibc:v1.0.1-rust1.94 image: cargo fmt --check, cargo clippy --all-targets --features server -- -D warnings, cargo clippy --target wasm32-unknown-unknown -- -D warnings, cargo check --features server, cargo check --target wasm32-unknown-unknown, cargo test --features server --bin vervain-app (15 passed).

Manual verification pending

The tunnel protocol is implemented against the agent source byte-for-byte, but a live agent + relay round-trip (listing, >100 MB download, upload progress, mkdir/rename/delete) has not been exercised in this environment. The one runtime assumption worth confirming on a live device is that Dioxus signal writes from the WebSocket onmessage callback re-render the listing (standard Dioxus pattern, but untested here).

## What Implements VAPP-7: enables the Files tab on the device detail page (previously an inert `DisabledTab`) as a live file browser. Reachable for online devices alongside the Terminal and Desktop tabs. ## How The prior run parked this because it assumed a control-channel `getdir` relay and a cross-origin `.ashx` download path, neither of which existed. Both dependencies have since landed: VA-3 (agent Files tunnel mode) and VS-3 (server file-tunnel allocator) are resolved, and the relay rendezvous is usage-agnostic (verified in vervain-server `meshrelay.rs`). So the Files tab rides the agent's Files tunnel (`usage=4`) exactly like Terminal (`usage=1`) and Desktop (`usage=2`): the new `open_files_tunnel` server fn reuses the shared `open_relay_tunnel(device_id, usage)` helper, the browser connects DIRECTLY to vervain-server's relay, and drives the agent's `run_files` protocol over the rendezvous WebSocket. No control-channel relay and no streaming Axum proxy are needed. Protocol implemented exactly from `vervain-agent::host::tunnel::run_files`: - `ls` directory listing (name / type / size / mtime, folders first) - `mkdir`, `rename`, `rm` (delete with a confirm dialog) - `download`: streams to disk via the File System Access API (`showSaveFilePicker` + a streaming `WritableStream`), writing one 16 KiB chunk at a time and acking per chunk for backpressure, so a >100 MB file is never held whole in memory. Browsers without the API fall back to a buffered Blob download. The File System Access calls go through `js_sys::Reflect`, so no `web_sys_unstable_apis` build flag is required. - `upload`: reads the local file slice-by-slice and is flow-controlled by the agent's per-chunk `uploadack`, with a progress indicator. Chunks are 0x00-escaped per the agent's JSON-disambiguation rule. ## UI Breadcrumb + Up navigation, folders-first listing with size and modified columns, New folder / Upload toolbar actions, and per-row Download / Rename / Delete. Offline devices and tunnel-open rights errors surface via the routing-ack mapping and a `LoadError` banner; the tab is disabled when the device is offline. ## Acceptance criteria - [x] Files tab lists directories with navigation, sizes, and modified times - [x] File download works for files >100 MB without loading the whole file into memory (File System Access API streams 16 KiB chunks straight to disk) - [x] Upload with progress indicator - [x] mkdir / rename / delete with confirmation on delete - [x] Rights and offline errors surface in-pane - [x] Linked as depends-on to VA-3 and VS-3 (already linked in YouTrack) ## Checks `just check` equivalents all green in the `rust-builder-glibc:v1.0.1-rust1.94` image: `cargo fmt --check`, `cargo clippy --all-targets --features server -- -D warnings`, `cargo clippy --target wasm32-unknown-unknown -- -D warnings`, `cargo check --features server`, `cargo check --target wasm32-unknown-unknown`, `cargo test --features server --bin vervain-app` (15 passed). ## Manual verification pending The tunnel protocol is implemented against the agent source byte-for-byte, but a live agent + relay round-trip (listing, >100 MB download, upload progress, mkdir/rename/delete) has not been exercised in this environment. The one runtime assumption worth confirming on a live device is that Dioxus signal writes from the WebSocket `onmessage` callback re-render the listing (standard Dioxus pattern, but untested here).
feat(device): per-device file browser tab (Files tunnel)
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 1m6s
cfc1942b72
Enable the Files tab on the device detail page (previously an inert DisabledTab) as a live file browser over the agent's Files tunnel (usage=4), mirroring the terminal (VAPP-5) and desktop (VAPP-6) panes. The browser opens the tunnel via a new open_files_tunnel server fn, connects directly to vervain-server's relay, and drives the agent's Files protocol (vervain-agent::host::tunnel::run_files, VA-3) over the rendezvous WebSocket.

The transport is the Files tunnel, not the control channel: VA-3 (agent Files mode) and VS-3 (server file-tunnel allocator) both landed, and the relay rendezvous is usage-agnostic, so the same open_relay_tunnel(device_id, usage) pattern works unchanged for usage=4. No control-channel getdir relay and no cross-origin .ashx proxy are needed.

Protocol implemented exactly from run_files: ls (directory listing with name/type/size/mtime), mkdir, rename, rm (delete with confirm), download, and upload. Download streams to disk via the File System Access API (showSaveFilePicker), writing one 16 KiB chunk at a time so a >100 MB file is never buffered whole in memory; browsers without the API fall back to a buffered Blob download. Upload reads the local file slice-by-slice and is flow-controlled by the agent's per-chunk uploadack, with a progress indicator. The File System Access calls go through js_sys::Reflect so no unstable web-sys build flag is needed.

UI: breadcrumb + Up navigation, folders-first listing with size and modified columns, New folder / Upload actions, and per-row Download / Rename / Delete. Offline devices and tunnel-open rights errors surface via the server fn's routing-ack mapping and a LoadError banner; the tab is disabled when the device is offline.

#VAPP-7
fix(files): cancel agent-side upload on local read failure
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 1m10s
Create release / Create release from merged PR (pull_request) Has been skipped
a9ba3cae57
If reading a local file slice fails mid-upload (e.g. the source becomes inaccessible), the browser cleared its own upload state but never told the agent, leaving the agent's upload half waiting for chunks until the WebSocket closed. Send an `uploadcancel` so the agent drops its half immediately. Factored the three upload-abort paths into one `fail_upload` helper.

#VAPP-7
nrupard deleted branch feat/file-browser-tab-vapp-7 2026-06-12 19:43:06 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-apps!34
No description provided.