* fix(hermes): resolve config dir via HERMES_HOME and platform defaults
On Windows, CC Switch hardcoded `~/.hermes` as the Hermes config directory,
but Hermes resolves it through `get_hermes_home()`: `HERMES_HOME` env var,
then a platform default (Windows `%LOCALAPPDATA%\hermes`, mac/Linux
`~/.hermes`). So CC Switch wrote configs where Hermes never reads them and
provider changes had no effect (refs #3178).
`get_hermes_dir()` now mirrors Hermes' own resolution order:
1. `settings.hermes_config_dir` — CC Switch explicit override
2. `HERMES_HOME` env var — trimmed, non-empty, taken as-is (no `~`
expansion, matching Hermes' `Path(val)`)
3. platform default — Windows reads the `LOCALAPPDATA` env var (the exact
value Hermes reads, not a re-derived Known-Folder path) and falls back
to `~\AppData\Local\hermes`; mac/Linux keep `~/.hermes`
Relation to #3470: that PR dropped `HERMES_HOME` for "consistency with
Codex/Claude". But unlike those tools Hermes treats `HERMES_HOME` as a
first-class mechanism (its own Windows installer sets it), so ignoring it
reintroduces the same mismatch for relocated installs. This keeps it.
Tests cover HERMES_HOME precedence, settings-override > HERMES_HOME, blank
HERMES_HOME fall-through, the platform-correct default, and a pure
`windows_local_hermes_dir` helper exercising the LOCALAPPDATA-set and
LOCALAPPDATA-empty fallback paths on every host.
* test(hermes): isolate config-dir tests from ambient HERMES_HOME/LOCALAPPDATA
`get_hermes_dir()` now consults `HERMES_HOME` (all platforms) and `LOCALAPPDATA`
(Windows). The `with_test_home` helper only neutralized `CC_SWITCH_TEST_HOME`,
so an ambient `HERMES_HOME` — which Hermes' own Windows installer sets — would
make tests that write `get_hermes_config_path()` escape the temp home and touch
a real Hermes config (and the same hazard exists via `LOCALAPPDATA` on Windows).
Clear and restore both env vars in `with_test_home`. This is safe: only this
module reads those env vars, and `dirs` uses the Known-Folder API rather than the
env var. Adds a test asserting both are neutralized inside `with_test_home`.
Addresses the Codex review comment on #4680.
* fix(hermes): trim LOCALAPPDATA to match Hermes' .strip()
`windows_local_hermes_dir` checked `OsStr::is_empty()` on the raw value, but
Hermes does `os.environ.get("LOCALAPPDATA", "").strip()`. A whitespace/padded
`LOCALAPPDATA` would make CC Switch write under the literal padded path while
Hermes trims and falls back, hiding the config from Hermes. Trim before the
empty check, matching the existing `HERMES_HOME` handling. Adds
`windows_local_hermes_dir_trims_localappdata`.
Addresses the Codex review comment on #4680.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
The updater's relaunch() (and app.restart()) triggers ExitRequested
with code RESTART_EXIT_CODE, which the handler treated as a regular
exit: it called api.prevent_exit() and spawned an async cleanup task.
However Tauri silently ignores prevent_exit() for restart requests
(see ExitRequestApi::prevent_exit docs), so the event loop keeps
shutting down regardless and fires every plugin's RunEvent::Exit hook.
Two threads then deadlock:
- the spawned cleanup task runs save_window_state on a tokio worker,
holding the window-state plugin's internal mutex while querying
window geometry, which dispatches to the main thread and blocks;
- the main thread, already inside the plugin's own RunEvent::Exit
hook, blocks on that same mutex.
The app freezes forever on the restarting screen with the update
already installed; force-quit + reopen comes back on the new version
(#3998). Confirmed on macOS by sampling the frozen process: main
thread parked in tauri_plugin_window_state save_window_state mutex
lock, tokio worker parked in is_maximized -> mpsc recv.
Fix: classify exit requests (None / restart / user exit) and let
restart requests fall through untouched to Tauri's default restart
flow (RunEvent::Exit -> re-exec). On that path window state is saved
by the plugin's exit hook on the main thread, the tray icon is
cleaned up by Tauri's internal cleanup_before_exit, and proxy/live
config restore is unnecessary because the new instance takes over
immediately. The regular exit path (tray quit) is unchanged.
With the fix, a simulated updater relaunch (request_restart) re-execs
the new process in under 200ms, 3/3 runs; normal quit still performs
full cleanup.
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>