Release v1.17.0 - #26
Merged
Merged
Conversation
…ndling and default config
…ndling and default config
…me method mountWithBody now accepts an optional volumePath parameter. When empty, existing behaviour is preserved (auto-select first volume from multiPaths). RequestAndMountVolume follows the same subscribe-request-wait pattern as RequestAndMount but accepts a specific volume path to mount. If the path is empty, available volumes are returned without mounting (list mode). Extracted buildVolumes helper to avoid duplicating the volume-building logic in Volumes() and the new method.
Adds sftp_browse IPC command with volume resolution logic (index, name, or path matching). The daemon handler requests fresh credentials, then either lists volumes or mounts the chosen one via sshfs. New IPC types: - CmdSftpBrowse constant - SftpBrowsePayload (deviceId + optional volume) - SftpBrowseResponse (optional path + volumes) New client method: - SftpBrowse(deviceID, volume) returns (mountPath, volumes, err) CLI: - kcd sftp browse <device-id> list volumes (fresh creds) - kcd sftp browse <device-id> <v> mount volume by index/name/path
Adds two new reference documents:
- docs/IPC_PROTOCOL.md — complete wire protocol reference (28 commands,
27 event types, packet tables, walkthroughs)
- docs/CLIENT_GUIDE.md — client developer guide with Python examples,
feature patterns, and integration templates
Updates docs/CLI.md:
- Adds sftp browse subcommand docs
- Adds mpris raw section
- Fills event type table with 6 previously missing types
- Fixes share.complete payload schema
Updates packaging/kcd.fish-completion:
- Adds missing event types to watch --events completions
Updates AGENTS.md:
- Adds step 8 to plugin checklist (documentation requirement)
The install hook printed a message on every package update which was annoying for users. Removing the reference skips it entirely.
Adds automatic cleanup of stale devices from the registry. Devices that are neither paired nor connected are removed when their last-seen timestamp exceeds the threshold (default: 15m). This prevents old discovery entries from accumulating in `kcd devices` and `devices.json`. - LastSeen is now refreshed on every TCP connection (handleNewConnection) so active/reconnecting devices stay alive - Prune runs at daemon startup and before every saveDevices write - New config key: prune_stale_threshold (Go duration format, '0' to disable) - Silent cleanup — no events published for pruned devices
Adds result/ to .gitignore and restores flake.lock after Nix build step to prevent git dirty state error in GoReleaser.
git checkout flake.lock only restores from the index, but if Nix also stages the file, the index copy is still dirty. Using git checkout HEAD -- flake.lock properly discards both staged and working-tree changes.
Keep HEAD -- flake.lock over plain flake.lock to properly discard staged changes.
Nix build creates flake.lock and a result symlink, which dirties the git tree and blocks GoReleaser's --clean check. Moving Nix steps to a separate workflow triggered by workflow_run eliminates this conflict entirely.
ReconnectBackoff shift overflowed int64 at attempt >= 54, returning 0 instead of capping, causing time.After(0) to fire immediately in a tight loop (25% CPU on one core). Fix 1: cap attempt at 31 before shift to avoid overflow entirely. Fix 2: add reconnecting guard to Device via atomic.Bool to prevent multiple concurrent reconnect goroutines from piling up.
systemd user services don't inherit desktop env vars, so
os.Getenv("WAYLAND_DISPLAY") was always empty, forcing xclip
even under Wayland.
Replace static isWayland bool with detectBackend() that probes
exec.LookPath("wl-paste") -> exec.LookPath("xclip") at first
use, cached via sync.Once.
Also log Warn when wl-copy/xclip fails instead of silent discard.
New plugin for controlling phone audio volume from the CLI — the inverse of the existing SystemVolume plugin (phone controls PC volume). - Handles incoming kdeconnect.systemvolume (sink list + per-sink updates from phone), sends kdeconnect.systemvolume.request (volume/mute commands to phone) - Caches sink list per-device via sync.Map; OnConnect auto-requests sink list - IPC: remote_volume_list, remote_volume_set, remote_volume_mute - CLI: kcd volume list/set/mute subcommands - Events: reuses volume.update for both per-sink and full sink-list payloads - Default: enabled (remotesystemvolume = true)
Under systemd user services, neither WAYLAND_DISPLAY nor DISPLAY is inherited from the login session. detectBackend() only checked exec.LookPath, found wl-paste, and committed to the Wayland backend — but wl-paste then defaulted to the wrong socket (wayland-0) and failed with exit code 1. Fix: check display environment variables first, then probe /run/user/1000 for any wayland-* socket before selecting the Wayland backend. X11 remains the fallback when no socket is found.
Adds [clipboard] config section with push_on_connect option (default false). When true, the local clipboard is pushed to a device every time it connects/reconnects via OnConnect. When false (default), clipboard is only pushed explicitly via kcd clipboard.
Clipboard stopped working on daemon restarts because the backend was probed once via sync.Once at first use. When the daemon boots before the compositor (systemd user default.target), the probe resolved to unknown and stayed cached for the process lifetime. - probeBackend: prefer a live WAYLAND_DISPLAY socket, else scan $XDG_RUNTIME_DIR for wayland-* sockets, else X11 fallback. - getBackend: re-probe while unknown, cache only non-unknown results. - clipboardCmd: inject the probed WAYLAND_DISPLAY into subprocess env and bound subprocesses with a 2s timeout so a hung wl-paste cannot stall clipboard sync. - kcd-user.service: After/PartOf/WantedBy graphical-session.target so the daemon starts after the compositor exports the display env.
A peer that keeps connecting then dropping (e.g. a dying phone) previously spawned a fresh auto-reconnect loop at attempt 0 on every disconnect, pinning the backoff at the 2s floor forever and hammering the peer. Persist the attempt counter on the device: a successful dial bumps it, and the next cycle continues backing off if the connection drops before reconnectFlapThreshold (15s). A connection that survives that long counts as stable and resets the counter on drop.
…errors clipboardCmd cancelled its 2s context immediately, so exec.CommandContext saw an already-cancelled context — its watchdog could SIGKILL wl-paste mid-flight, making clipboard pushes flaky. The timeout test passed for the wrong reason (instant kill, not the timer). - clipboardCmd no longer owns a deadline; runClipboard bounds execution with the 2s timeout and defers cancellation correctly. - runClipboard captures stderr and wraps it into the error, so failures report the real reason (e.g. 'Nothing is copied') instead of a bare 'exit status N'. - Push treats an empty/no-selection clipboard as nothing-to-push instead of a hard error, so kcd clipboard and --watch don't spam at startup before anything has been copied. - Tests: rework the timeout test to guard against instant-cancel regressions and cover stderr surfacing, no-selection detection, and empty/success push paths.
wl-copy forks a persistent manager that inherits the stdout/stderr pipes, so cmd.Output() blocked past the deadline and inbound copies were silently dropped; pushes then resubmitted stale or empty content to the phone, producing a bogus 'pasted from your clipboard' toast. Write with runCopy (fds pointed at the null device) so Run() returns once wl-copy exits. Also write clips with wl-copy -n and normalize trailing newlines in the dedup guard so a tooling newline cannot turn the phone's own content into an outbound echo.
…g reads runClipboard/runCopy copy WaitDelay from the passed cmd, but a bare exec.CommandContext defaults it to 0 — which means cmd.Output() blocks on the stdout pipe until the last fd holder exits. A killed child that spawned a grandchild retaining the pipe (sh -c 'sleep 30 & wait' — and /bin/sh on CI runners) kept tests and reads pinned for the full 30s. Cap the delay at 1s so Wait always returns promptly after the 2s deadline.
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.
Release v1.17.0
Headless KDE Connect daemon for Linux. This brings 28 commits onto
mainsince v1.16.0, mixing new plugins and a batch of clipboard/notifications reliability fixes.New Features
kcd volumeto control the remote device's system volume~/.cache/kcd/art/)kcd sftp browsesubcommand +volumePathparameter forRequestAndMountVolumekcd watch: json-mode auto-reconnect on disconnectBug Fixes
wl-copyforks a background manager that inherited our stdout/stderr pipes, so cleanup hung and inbound copies were silently dropped; pushes then re-sent stale/empty clipboard to the phonewayland-1; detect backend whenWAYLAND_DISPLAYis unsetgraphical-session.targetso the clipboard works on bootflake.lockfrom HEAD, resolve release.yml conflicts