How vmup builds an SSH batch
Collect files, stage them under a batch id, stream them over ssh into a partial remote dir, rename into place, and copy the prompt to the clipboard.
vmup’s job is a single remote directory the agent can open. The original filenames on your laptop are not that directory. This page is the pipeline, not a recipe. Commands: Upload files.
Collect
Four inputs, one staging session:
- Paths you passed, including one level of directory children.
- Picker (no args).
- Clipboard loop (
--clip). - Folder watch (
watch/--watch).
--clip and --watch cannot run together because both hold stdin.
SSH preflight starts as soon as the session exists, in parallel with collect. A long picker does not block the first ssh call. The result is awaited after collect, before upload.
Stage
A batch id is minted:
agents-YYYYMMDD-HHMMSS-<uuid>Files are copied to ~/.cache/vmup/<batchId>/ and renamed image-NN, video-NN, file-NN. Counters are per kind, so two PDFs become file-01.pdf and file-02.pdf even if an image was staged between them.
The rename is deliberate. Agents get a stable listing: images together, then other files, instead of Screenshot 2026-09-11 at 2.01.28 PM.png mixed with notes (1).pdf. You lose the original basename on the VM. If you need the name, keep local staging (--keep-local) or send a manifest yourself.
Hidden junk is dropped at collect for directories and watch. Validate then applies size, count, and type rules.
Preflight
ssh runs mkdir -p <remote_dir> && test -d <remote_dir> && echo OK with a 20s timeout. Direct mode also checks the private key exists and is not group/world-readable.
Failure here keeps local staging and skips the upload. Exit 3.
Upload
Remote layout:
rm -rfthe final dir and a sibling*.partialmkdirthe partial dir- Stream each staged file over
ssh(cat >the remote path, local file on stdin) into<remote>/<batchId>.partial/ mvpartial → final
Agents never see a half-written folder. A failed upload removes the partial dir. Local staging stays so you can look at what would have been sent.
Progress on a TTY:
⠋ Uploading 3 files 1.2 MB / 4.5 MB 4sThe right-hand size is the sum of local stats. The left-hand size is bytes ssh has accepted on stdin, including the current file, so a single large file ticks up during the transfer. JSON mode still animates stderr when it is a TTY.
After success
Local staging is deleted unless --keep-local.
vmup then:
- Client delete: a detached
sleep <ttlMinutes * 60>(minimum 60s) thenssh rm -rfthe batch. This only runs while this process’s machine stays up. - Sweeper probe:
test -x ~/vmup/.cleanup.sh. If missing, human mode warns. - Prompt:
prompt_templatewith{{remote_path}}set to the final dir plus/. - Clipboard:
clipboard_copy(promptby default, orpath, ornone) viapbcopy/wl-copy/xclip.--jsonskips this.
To delete batches from this laptop without waiting: vmup prune (expired) or vmup prune --all (every agents-* dir). Recipes: Clean up batches.
The agent does not run vmup. You paste. The default sentence is Please inspect all files in <path>.
Ctrl+C during collect deletes staging and exits 130. After upload has started, interrupt handling is the default Node behavior plus whatever OpenSSH does; the partial-dir cleanup is on upload failure, not on SIGINT mid-transfer.