The watchdog was spawned inside snapshot.run, so legacy highlights mode ran
without it. That is the mode NickelMenu invokes, against the endpoint that
stopped responding and hung a run for 16 days — so the exact failure being
fixed was still reachable from the device's menu.
Moves the spawn into main, ahead of mode dispatch, so both paths are covered.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds `kb_exfiltrator snapshot <db> [endpoint] [timeout-secs]`, which uploads a
consistent copy of the whole database instead of just extracted highlights. The
server can then parse whatever it wants, so adding a new statistic never means
touching the device again.
Uses VACUUM INTO rather than copying the file. Nickel keeps the database in WAL
mode and recent commits live in KoboReader.sqlite-wal until a checkpoint, so
copying the bare .sqlite silently loses reading progress — measured at 19 hours
of drift on the live device. Copying all three files instead is non-atomic: a
checkpoint landing mid-copy yields a pre-checkpoint main file plus a post-reset
WAL, losing data with no error. VACUUM INTO runs in a read transaction, so it
sees WAL-resident commits and emits one compacted journal_mode=delete file.
Also fixes the failure mode that left a process hung for 16 days: the server
stopped responding and std.http.Client has no timeout, so the process blocked
forever while holding the SQLite handle open. Snapshot mode closes the database
before any network I/O, and a watchdog thread hard-exits after a deadline
covering the whole run (snapshotting can stall on a locked database too).
Two portability constraints, both specific to the Kobo Elipsa:
- Linux 4.9.77 predates statx (4.11). Zig's File.stat() and getEndPos() both
issue it, returning ENOSYS as a bare error.Unexpected — after the snapshot
is already written, so it presents as a post-write failure. readToEndAlloc
with no size hint uses plain read() calls instead.
- usize is 32-bit on armv7, so a u64 stat size will not coerce. Only the ARM
build catches this.
Legacy invocations are unchanged, so the existing NickelMenu item keeps working.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Add text cleaning to remove page reference numbers (e.g., "10After" -> "After")
- Implement cleanHighlightText() function with boundary detection
- Add NickelMenu configuration for UI integration
- Create sync wrapper scripts (with and without FBInk notifications)
- Add installation guides for Kobo deployment
- Include pointer/slice explanation examples for learning
Features:
- Extracts highlights from Kobo SQLite database
- Cleans page references from academic texts
- Computes SHA-256 hashes for deduplication
- POSTs JSON to Flask API endpoint
- Supports ARM static linking for Kobo hardware
- Optional FBInk notifications for user feedback
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Highlight struct to represent bookmarks with owned memory
- Implement SHA-256 hashing for text and book identification
- Update SQL query to extract clean book filenames (removes path and fragment)
- Remove unused Annotation column from query
- Add CLAUDE.md for repository documentation
- Update .gitignore to exclude SQLite WAL files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>