[3.6.6] - 2026-04-27
Agent surface, onboarding polish, server speedtest, community wishlist
The full v3.6.6 cycle ran in roughly 36 hours and is shaped by two parallel community feedback waves. Issue [#125] (@EhudKirsh) — a clean reproduction of CLI/Terminal bugs — prompted a 4-Sonnet black-box audit of the AI-agent surface that surfaced 13 friction points; the same thread invited Ehud to point an AI assistant at agent-info --json and agent-bootstrap --json, and the audit closes that loop. Issue [#130] (@scottonanski) reframed the first-run empty state, and most of the onboarding polish ships in response. Issue [#129] stayed open as the v3.6.6 wishlist thread, and many of the small UX wins below answer specific items voted up there.
Added
aeroftp-cli agent-connect <profile>+ MCP toolaeroftp_agent_connect— single-shot connect surface for AI agents. Returns one JSON payload with per-block status (connect,capabilities,quota,path), replacing the boilerplate sequenceconnect → about → df → ls /. Block status values areok/unsupported/unavailable/error; agents readconnect.statusfor the go/no-go decision and degrade gracefully on the rest. Live-connect allowlist (FTP / FTPS / SFTP / WebDAV / S3 / GitHub / GitLab) is documented inline in--help. For protocols outside the allowlist (pCloud, Filen, Dropbox, etc.) the response still includes validcapabilities,pathandprofileblocks; only theconnectblock reportsstatus: "unsupported"and the CLI exits 0 because the rest of the payload is still actionable. New lib moduleagent_session.rsis the single source of truth, also translating profile.options camelCase keys (bucket, region, tlsMode, privateKeyPath, …) into ProviderConfig.extra snake_case so S3 / Azure / SFTP-key / FTPS profiles connect via vault for the first time on the agent path.agent-info --jsonexposes aprotocol_featuresmap keyed by protocol → list of feature tokens (share_links,resume,server_copy,versions,thumbnails,change_tracking, etc.) plus anagent_connect_supported_protocolsarray. Collapses what used to require Nagent-connectcalls (one per profile) into a single batch query — driven by the audit's Battery D where one agent had to walk 69 profiles to discover share-link capability.head -c / --bytes <N>byte-range preview for remote files. Works on binary content (returns base64 in JSON when bytes aren't valid UTF-8) and reportsbytes_returned/total_size/truncated/encoding. Closes the audit's Battery A "no way to get the first 4KB without a full download" gap.ls --limit N,--files-only,--dirs-onlyflags. Summary now carriestruncated: boolandtotal_before_limit: intso agents can detect partial results unambiguously. Same trio added tofind, plus a--name <glob>alias for the positional pattern (the natural first-attempt form for agents who expect named args).- Cross-profile multi-select on My Servers — click a server card body to toggle Cross-Profile selection (icon button still triggers Connect). Up to two cards selected; third click drops the oldest FIFO. Indigo ring + arrow-up-right badge for source, emerald + arrow-down-left for destination. Always-visible Cross-Profile button in the toolbar with three brightness states (0 / 1 / 2 selected) and a counter badge. Right-click context menu offers Set as source / destination for direct assignment. Drag-to-reorder now works while a filter chip is active or a search query is typed.
- Server SpeedTest — new isolated benchmark dialog and CLI parity. GUI: Single / Compare tabs, multi-select up to 8 servers, parallel selector, ranked compare table with tri-state integrity, history summary card with regression warning (last < median × 0.7), Esc cancels running test. CLI:
speedsubcommand upgraded with random non-compressible payload (was zeros), SHA-256 integrity, TTFB measurement,--no-integrityflag,--json-out. Newspeed-comparesubcommand emits JSON v1, CSV, and Markdown reports.redact_url_for_display()is applied to every report and error path so passwords never appear in stdout / JSON / CSV / MD;csv_cell_safe()neutralizes spreadsheet formula triggers (=,+,-,@);md_cell_safe()escapes pipes / newlines / backslashes. Streaming download toNamedTempFilewith TTFB measurement, tempfile pre-allocation before connect to eliminate orphan/leak windows, SQLite history (WAL, 0600/0700 perms, 1000-row cap,server_nameforced NULL at insert as defense in depth), spawn_blocking for SHA-256 hashing and random payload generation. 22 speedtest lib tests + 6 CLI tests forredact_url, CSV/MD escaping. Schemaaeroftp.speedtest.v1is stable across CLI and GUI. - Selected-server chips on the Compare tab — each chip shows the display name, protocol tag, and an inline X to remove the server from the comparison without scrolling the list.
- Provider IconPicker dialog (closes Ehud's wishlist ask in [#129] modeled on KeePassXC's icon library) — replaces the bare file dialog under "Choose icon" with a structured picker. "Provider icons" tab shows every entry in
PROVIDER_LOGOSgrouped by catalog category with curated popularity priority; "Custom icons" tab is the user's persisted library (localStorageaeroftp-custom-icons) with per-icon trash button. Live free-text search. SVG support throughout: Tauri file dialog accepts.svgand stores them asdata:image/svg+xml;base64,…data URLs without canvas rasterization, preserving vector fidelity at every render size. Always consumed via<img src>so embedded scripts are sandboxed. Live re-detection on dialog open: "On server" card auto-firesdetect_*_faviconand surfaces the live result, with a "Matches server" check on the In Use card when the live and saved values agree. - Server Health Check legend — dialog header shows the threshold dots inline (green 80+, yellow 50-79, red <50) instead of only on hover; ScoreGauge stacks
/100under the numeric score. Closes Ehud's specific "name the boundaries" ask in [#129] for both single-server and Check All runs. - Two WebDAV provider presets (closes Ehud's [#129] suggestions): OpenDrive WebDAV (
opendrive-webdav, serverhttps://webdav.opendrive.com, regular login password) and Yandex Disk WebDAV (yandexdisk-webdav, serverhttps://webdav.yandex.ru, app-specific password with deep-link toid.yandex.com/security/app-passwords). Both appear automatically in the Discover catalog under WebDAV. passwordGenUrlfield onProviderConfig— surfaces in the connection-form footer as a "Generate password" link (amber, key icon) next to the existing Create Account / Docs links. Wired up for Koofr WebDAV and Yandex WebDAV.PasswordStrengthBarin the Export Encrypted Backup flow — same component as AeroVault, gives users a 0-100 score with color-coded feedback while typing. Was previously only inside Vault; parity was the user-visible ask.- File associations for
.aeroftpand.aeroftp-keystore— TaurifileAssociationsextended withapplication/x-aeroftp(server-profiles export bundle) andapplication/x-aeroftp-keystore(full keystore). Linux MIME XML extended with both new types alongside the existing AeroVault entry. Per-format icons not shipped yet — file managers fall back to the generic AeroFTP icon. - "Close to tray instead of quitting" Settings toggle (closes Ehud's [#129] follow-up). When enabled, clicking the window close button hides AeroFTP to the system tray instead of terminating, so background tasks like AeroCloud sync keep running. Independent from "Start minimized on autostart"; default off to preserve existing behaviour. Esc on either panel now clears file selections when no modal/dialog/preview is open, mirroring the right-click-empty-area gesture.
mkdir --jsonreportsalready_existed: boolso audit trails distinguish "I created it" from "it was already there" on idempotent-pinvocations.- Inline saved-profile inventory in
agent-bootstrap --jsonplus per-profileauth_state(valid/expired/needs_refresh/no_credentials/unknown) wired into all three list surfaces (profiles --json,agent-bootstrap --json, MCPaeroftp_list_servers). Eliminates the round-trip an agent had to do afteragent-bootstrapand stops connect-then-fail loops on profiles whose OAuth tokens expired silently. Newprofile_auth_statelib module is the single source of truth; pure local, never touches the network.
Changed
- First-run empty state on My Servers (issue [#130],
@scottonanski) — visible "+ New" header label instead of icon-only+. Empty state now reads "Get started" with a primary CTA "Add your first server" (was "Quick Connect", which read as "rapid versus what?") and a 2x3 category grid (Protocols / S3 / WebDAV / Cloud / Media / Developer) that jumps straight into the relevant Discover slice in one click. - Cross-Profile gating — toolbar button, card click selection, and right-click "Set as source / destination" menu items are now gated on
servers.length > 1. Selecting a single server as source/destination was nonsensical and used to ship a confusing badge in the toolbar. - Discover service cards — replaced the responsive
grid-cols-1/2/3/4/5cascade withauto-fill minmax(260px, 1fr). Earlier breakpoints were truncating provider names to "Cloudflar...", "Backblaz...", "S3 Co...". The auto-fill grid keeps each card readable and only adds columns when the panel actually has room. WebDAV preset logos (OpenDrive, Yandex Disk) mapped to the existingPROVIDER_LOGOSso the new presets show proper icons everywhere (Discover, ConnectionScreen, SessionTabs, SettingsPanel, MyServers). - My Servers grid breakpoints —
grid-cols-2 md:3 lg:4 xl:5 2xl:6withp-1breathing room so the selection ring is no longer clipped at the edges. IntroHub container spans the same width as the connected file manager view, so switching between IntroHub and an active session no longer produces a visual jump. - Titlebar Connect/Disconnect anchored leftmost (Ehud, [#129]) — Connect/Disconnect now sits at the leftmost position of the right toolbar with its own trailing separator. Previously it was placed after the Vault / Lock / Settings trio, so every connect/disconnect shifted those three icons sideways and made it easy to land on Lock or Settings instead of the button you aimed for. Now the dynamic button enters and exits on the left without nudging anything else.
- Redundant gray protocol badge collapsed on server cards — for non-FTP/FTPS/SFTP protocols the displayProto badge was rendering gray and duplicating the colored protocolClass badge that follows it (e.g. "WEBDAV" gray + "WebDAV" purple, "S3" gray + "S3" orange). The protocol badge now renders only when it carries dedicated color; otherwise the provider icon and colored class badge already convey the info.
getwarns on stderr when the resolved local destination already exists (audit Battery C). Suppressed under--quiet,--immutable, and--json(the JSON envelope already includes the destination path so agents can diff against their own state).staterrors with exit code 2 emit a singlestat failed: <path> not foundinstead of the cascaded four-layer chainPath not found: File not found: No such file: No such filethat was leaking through nested provider error wrappings. Exit code 2 is unchanged.- "Using profile:" banner suppressed on stderr when
--jsonis set via a globalJSON_MODEatomic. Earlier behaviour mixed the banner into combined-stream captures, corrupting JSON parsers. Surfaced by every audit agent — Battery C lost a verify-read step to it. - AeroRsync delta transport wired into cross-profile transfer and DevTools save_remote_file for SFTP destinations with key-based auth.
cross_profile_transfer::copy_one_filenow consultstry_delta_transferon the destination provider before falling back to the classic temp-file upload.save_remote_file(DevTools Code Editor save button) gets the same treatment — a save against an SFTP destination with key-based auth ships only the diff. Big win for files in the 50KB-50MB range that change in small spots (CSS, JSON, YAML, TOML, log tails, source). Both paths are SFTP-only today and gated by the existingnative_rsync_enabledruntime toggle; other providers fall through unchanged. New opt-in E2E delta path docker harness (AEROFTP_SEED_AERORSYNC_E2E=1) seeds two SFTP profiles pointing at alinuxserver/openssh-server+ rsync container with the mandatory non-ed25519 host-key removal that works around the U-02 libssh2/russh negotiation asymmetry. - CI: legacy macOS Intel build target dropped (
x86_64-apple-darwin). Recurring source of flakybundle_dmg.shfailures without a matching audience — Apple Silicon (macos-latest, aarch64) stays as the macOS leg; Intel users with demand can pin to v3.6.5 or earlier. Drops the orphanedmacos-intelartifact path from the cosign sign loop, the DMG-rename step, and the per-target release-upload step. README Platform Status table dropped the corresponding row. If Intel demand returns, restoring the matrix entry is a 6-line revert. - Quota block in
agent-connectauto-downgrades tounsupportedwhentotal_bytes == 0. Some WebDAV servers (Koofr WebDAV, certain InfiniCloud configs) report 0/0 when their backend has no quota API, indistinguishable from a genuinely empty account. Now agents see the right signal. agent-connectexit code 0 when capabilities are still valid even if the live connect block reportsstatus: "unsupported"(protocol outside the allowlist). Previously this returned exit 1 even though the JSON body was perfectly usable. Audit Battery D's pCloud detour was the canonical case.profiles listnow works as a no-op alias ofprofiles— same ergonomics asalias list; the muscle memory was tripping people up.
Fixed
- XML entity decoding broken on listings across S3 / Nextcloud-WebDAV / Azure Blob / Jottacloud — file/object names containing characters that get XML-escaped on the wire (
&,',<,>,") were either truncated, doubled, or stripped. Concretely:&in S3/WebDAV/Azure listings emitted as&made quick-xml fire a separateEvent::GeneralRef("amp")between text fragments, and the parsers (which assigned the last fragment instead of accumulating) silently dropped the leading half (a&b.txtlisted asb.txt). Storj specifically uses the numeric form'instead of'for', and a builtin-only translator dropped it (a'b.txtlisted asab.txt). Jottacloud stores file names as XML attributes; quick-xml does not auto-resolve entities on attribute values, so the raw&was making it through (a&b.txtlisted asa&b.txt). Newproviders::xml_textmodule exposesxml_entity_to_str(the five XML builtins + decimal/hex numeric character references) andattr_value(entity-aware attribute decode). Six text-extraction call sites in s3.rs, webdav.rs (three parsers) and azure.rs (list + error) converted from "assign last fragment" to "accumulate fragments + handle GeneralRef". SevenString::from_utf8_lossy(&attr.value).to_string()sites in jottacloud.rs swapped forxml_text::attr_value(&attr). Validated live against Storj S3, InfiniCloud WebDAV, CloudMe WebDAV and Jottacloud with a 13-name fixture (&,',<,>,", space,perché.txt,🚀,中文,%,#,+=); all four providers now agree with rclone byte-for-byte. - Jottacloud paths with
+,#or%were 404ing —jfs_url()(andtrash_url()) concatenated raw paths into the request URL without per-segment percent-encoding. JFS gateway interpreted+as space (a+b=c.txt→ 404), reqwest treated#as the start of a URL fragment (cool#1.txt→ 404, server sawcool), and100%.txtwas wrong on round-trip too. Per-segmenturlencoding::encodealready used by the upload path is now hoisted into the URL builder so list / get / stat / rename / trash all benefit. Slash separators kept; only segment contents encoded. Unit test covering+#%space&locks in the expected%2B/%23/%25/%20/%26output. - Cross-Profile Transfer dialog seeded source with the display name instead of the saved server id — clicking the remote-panel button opened the dialog without the active connection pre-filled. Fixed by passing the actual
savedServerIdthroughConnectionParams/FtpSessionand using it for source seeding. - Stale Cross-Profile selection after deleting saved servers —
crossProfileSelectionis now cleared when the saved-server count drops below 2. Previously, deleting the last server left a stale selection pointing at a deleted id and the toolbar badge persisted across the empty state until a manual refresh.
i18n
- 4 new IntroHub keys (
newConnection,getStarted,addFirstServer,browseCategory) for the onboarding rework, 3 new connection / health-check keys (connection.generatePassword,healthCheck.scoreLegend,healthCheck.scoreLegendDetail), 47 SpeedTest phase-1 + 42 phase-2 keys plus 1errorCompareTooMany, 2 close-to-tray keys (closeToTray,closeToTrayDesc), 6 cross-profile multi-select / IntroHub layout keys. All translated across the 46 non-en locales (Armenian spot-checked for Unicode integrity);npm run i18n:validatepasses with 0 placeholders remaining.startMinimizedOnAutostartDesclost its em-dash per project style rule.
Tests + tooling
- 13 unit tests in the new
agent_session.rsmodule covering the lookup error path, capabilities map, canonicalization, profile/quota/connect block helpers, and the camelCase→snake_case options translation. 22 speedtest lib tests + 6 CLI tests forredact_url, CSV/MD escaping.cargo clippy --all-targets -- -D warningsclean across lib + CLI bin. Black-box agent audit harness produced 4 functional batteries (search, multi-server inventory, write/read rendezvous, capability decision) plus 1 internal-Sonnet and 1 external Kilo-Auto-Free verification round; all 6 agents completed their tasks, and the post-fix verification confirmed zero blockers.
Downloads:
- Windows:
.msiinstaller,.exe, or.zipportable (no installation required) - macOS:
.dmgdisk image - Linux:
.deb,.rpm,.snap, or.AppImage