Windows publishes the core by moving the running binary aside, and only the
success path could delete it afterwards. A failed restart left it on disk,
and the uninstaller did not know about it either.
mihomo's updater truncates its own running executable in place; macOS kills
that under Hardened Runtime and leaves a 0-byte core. Verge now downloads the
release, verifies a staged copy next to the target, and renames it into place,
which never touches the inode the running core executes from.
Closes#6834
The startup fallback persisted its replacement, so a port blocked once was never
asked for again. It now overrides the Runtime Config for the session only.
Most of those conflicts were not real: SO_REUSEADDR cannot reuse a TIME_WAIT
socket owned by another user, and a service-managed core runs as root while the
app does not. A refused bind is now confirmed by asking whether anything answers.
Also restores the borrowed port when saving proxy ports fails.
Each auto-update reloads the core, which drops every connection running through a
`proxy-providers` entry. Existing profiles are corrected once at startup; the form
warns about shorter intervals but still saves them.
The queue got a full interval from now while the UI counted from `updated`, so a
restart pushed every deadline back and the countdown could read "update failed".
Retiring a task disabled mid-update also keeps its state, so re-enabling it no
longer starts a second run alongside the first.
Add a lifecycle lock around core start, stop, restart, and service handoff
paths. Make start_core idempotent, skip startup while exiting, and roll
back running mode after startup failures so later retries are not blocked.
Wait longer for slow Windows service startup, then hand off from sidecar
to service when it becomes ready. Retry service core startup during the
handoff window, and clean up root-owned macOS service core/IPC leftovers
before uninstalling the service.
Pulls in the plugin's response-deserialization tolerance, connection-pool permit fix, single reqwest::Client reuse, and WebSocket secret encoding/redaction (cf97ff9).
Recover from controller read/switch failures on the mode card instead of
showing a bare "communication error":
- fall back to the tolerant get_clash_mode command (then runtime config)
when the live /configs mode is unavailable,
- optimistically reflect the clicked mode and write it through to the
getClashConfig cache so a failed refetch can't flash back the old value,
- surface patch failures via showNotice on both the home card and the
proxies page (patchClashMode can now reject), and
- revalidate getClashMode/getRuntimeConfig on refresh-clash-config so a
once-empty fallback can recover.
The home mode card read the proxy mode only from mihomo's /configs (strict
BaseConfig); when that response drifted a single field, the whole read
failed and the card showed a bare "communication error". patch_clash_mode
also swallowed backend PATCH failures and always returned Ok, so the UI
could not tell a switch had actually failed.
- add a tolerant get_clash_mode command that reads mode from the saved
clash config (IClashTemp::get_mode), bypassing strict BaseConfig
deserialization, and
- make change_clash_mode return Result and have patch_clash_mode propagate
it; the hotkey/tray callers log-and-ignore as before.