EasySkills 3.2.0
EasySkills 3.2.0
A hardening release focused on robustness, data safety, and correctness across
the deploy engine, installers, background supervisors, and the WebUI — the
result of a full multi-round audit. No behaviour or API changes; all fixes are
drop-in. All 98 contract tests pass.
Process Termination Safety (macOS / Linux)
Three sites matched a backend process purely by a webui.py path appearing on
its command line, then force-killed the match. That also matched editors,
greps, and language servers that had the file open — and kill -9 would
destroy unsaved work.
own_webui_pid/stop_own_webui(deploy.sh) andown_webui_pid
(webui-service.sh) now require the matched process to be a Python
interpreter (ps -o comm=basename check) before killing. The uninstaller
(uninstall_mac.command) gained the same guard for bothbash/shand
pythonbackends.
Concurrency (Windows)
- Recover from an abandoned deploy mutex.
deploy.ps1Acquire-Locknow
catchesAbandonedMutexException. Previously, a single force-killed deploy
(Task Manager, hard reboot mid-sync) left the named mutex abandoned, and every
subsequentdeploy.ps1invocation threw the unhandled exception, re-abandoned
the mutex, and bricked all future deploys until a reboot. macOS/Linux already
self-healed via PID-lock recovery; Windows now matches that behaviour.
Data Safety
- Atomic config writes (Windows).
Write-Utf8NoBomindeploy.ps1now
writes to a temp file and atomically moves it over the target, mirroring
deploy.sh's temp+mvpattern. A directWriteAllTexttruncated-then-wrote
custom-targets.txt/disabled-targets.txt, so an interruption could leave
them truncated and silently drop every persisted custom agent path. - Verbatim custom-targets preservation (
install.sh). The installer no
longer round-tripscustom-targets.txtthrough a shell variable
($(cat …)+echo "$VAR"), which mangled paths containing backslashes,
glob characters, or a leading-. It now copies the file verbatim (matching
disabled-targets.txtandwebui.py'sdo_self_update).
Status & Labelling Correctness
deploy.sh --statusno longer false-reports a running watcher.
launchctl listprints-in the PID column for a job that is loaded but not
running; that was reported as✅ Running (PID -). Now treated as not-running,
consistent withget_watcher_statusinwebui.py.- Trae/Trae-CN AppData paths labelled correctly in fallback mode (Windows).
Indeploy.ps1's prefix-basedGet-AgentNamefallback,%APPDATA%\Trae\skills
was shadowed by the broader$Home\prefix and mislabelled "Custom Agent". The
more specific$env:APPDATA\prefix is now tested first, with explicit
Trae\*/Trae-CN\*branches.
Uninstaller Safety (macOS)
- Warn when symlink cleanup fails.
uninstall_mac.commandnow captures the
exit code ofdeploy.sh --cleanup. On failure it prints a clear warning with
a manual-cleanup command before trashing~/EasySkills, so a partial cleanup
no longer leaves dangling symlinks scattered across every agent's skills
directory with no indication.
Installer Reporting (Windows)
- Fix empty version reporting.
install_windows.batreadOLD_VERSION/
NEW_VERSIONwith%VAR%inside the parenthesised install block, where CMD
expands once at parse time — so every install printed an empty version and
upgrades were undetectable. The version report is now emitted after the
block, where the variables hold their real values.
WebUI Supervisor (Windows)
- Don't restart-storm when the port is held by a foreign process.
webui-service.ps1now mirrorswebui-service.sh: when port 6633 responds but
nowebui.ps1from this install owns it, it waits instead of relaunching —
preventing the supervisor from burning through its restart throttle with no
chance of recovery.
WebUI Frontend
- Surface backend failures on read polls.
apiCallpreviously swallowed
network/parse errors for GET routes (/api/status,/api/skills, …) with no
toast, so a dead backend left the dashboard looking alive. GET failures now
show a localised error toast, rate-limited to once per 15 s (with re-announce
after recovery) so the 5 s poller can't spam. - Localise the agent-path-edit error.
saveCustomModalEdit's catch no
longer shows the raw browser error string; it shows a consistent localised
message. - Harden the central XSS boundary.
escapeHtmlnow coercesnull/
undefined/ non-strings to''(viaString(text)), so a future optional
backend field rendered without a|| ''guard can't throw and break the
entire render loop.
Validation
- All 98 contract tests pass (version/agent-count assertions derive from
_maintenance/.versionandagents.json, so they stay green on release).