* fix: add SHA256 verification to prevent corrupt partial download loop
Fixes issue #858 — corrupt .partial files caused an infinite loop where
a failed/cancelled download would resume from EOF, pass extraction, and
loop forever with no way to recover without manual file deletion.
Three-part fix:
- Add sha2 crate and compute_sha256() helper; verify hash post-download
before extraction/rename for all 15 built-in models. Hash mismatch
deletes the .partial so the next attempt starts fresh.
- Delete .partial on extraction failure (independent of checksums) so
corrupt archives can't re-enter the loop via the same path.
- Emit model-download-failed event from the command handler so all
callers (ModelsSettings, Onboarding) get consistent error feedback
via a central store listener rather than per-caller return-value checks.
SHA256 hashes hardcoded alongside each model's URL in ModelInfo.
Custom models (sha256: None) skip verification silently.
* chore: remove doc comment from sha256 field
Doc comments on exported specta types propagate into bindings.ts as
inline JSDoc — not useful in generated TS. Removing at the source.
* test: add SHA256 verification unit tests
Extracts inline verify logic into ModelManager::verify_sha256() and
covers all four branches:
- None expected hash → skipped (custom models)
- Matching hash → ok, file kept
- Mismatched hash → error returned, partial file deleted
- Unreadable file → error returned
Ensures corrupt partial downloads are always cleaned up so the next
attempt starts fresh.
* chore: cargo fmt
* fix: clear cancel flag on sha256 failure, spawn_blocking for hash, fallback cleanup
- remove cancel_flags entry on sha256 failure to prevent a stale flag
from immediately cancelling the next retry attempt (HIGH)
- add fallback state cleanup in modelStore when result.status != ok,
so spinner clears even if model-download-failed event is missed (HIGH)
- run compute_sha256 in spawn_blocking to avoid stalling the async
executor while hashing large model files (up to 1.6 GB) (MEDIUM)
* emit some more events for the ui to pick up
* add translations + format
---------
Co-authored-by: CJ Pais <cj@cjpais.com>