Skip to content

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 13 Sep 08:05
· 49 commits to main since this release

Added

  • db rows <collection> prints the cached rows of any collection, as stored, in both output modes: the cache-side twin of fetch, with the same range flags and defaults (--day, --from/--to, --days; today by default; none for ring). Eight of the seventeen collections were write-only from the user's side: sync filled resilience, VO₂ max, bedtime guidance, sessions, rest mode, tags, ring and battery, db stats counted them, and nothing read them back short of fetch, which goes to the API and ignores the cache. The table view shows one column per stored column, numbers right-aligned, text cells cut at 40 characters and, on a terminal, squeezed further so the table fits the screen; a column name is never cut, and when the names alone would not fit (sleep-periods has 19 columns) each row is printed as a block of name value lines instead. --limit N prints the first N rows and the title says how many the range holds; a day of heart rate is hundreds. A newline in a tag comment shows as ⏎ instead of breaking the row. The JSON is the rows exactly as stored, so a JSON blob such as contributors stays a string. Heart-rate and battery samples are bounded on the local day asked for, the way fetch bounds its request, not on the UTC date their day column stores. An empty result names the range asked for and how to fill it. The describe manifest lists the subcommand with the collection enum. (#73)

Fixed

  • doctor no longer calls a cache current while report and db week mark its newest day as still accumulating. The data check compared calendar days and only warned past two, so on 2026-09-12 a cache ending on 2026-09-10 passed. It now measures hours from the end of the newest day (its local midnight in the cache timezone) and warns past 36: one missed night is ordinary ring lag, a second is worth the nudge. The detail names the limit, and nextStep points at oura-cli sync. (#114)
  • Text output is tidier. Every view draws its horizontal rule at the same two-column indent as the text under it (db today, db week, db trends, db stats, doctor and the panel after sync drew theirs at column 0), as wide as the widest line it delimits (a fixed 50 sat under 63-column trend rows), and never wider than the terminal, where it wrapped onto a second line. db week no longer pads its last column, which left up to nine trailing spaces on every row. The empty-today panel after sync explains that Oura publishes a day's summary once the night's sleep has synced, as db today already did, instead of printing bare dashes. oura-cli db on its own, db toady, and db --format json with no subcommand name the subcommands db takes and point at oura-cli db --help rather than the root help; on a terminal a bare oura-cli db prints that help, as a bare oura-cli does. (#61)
  • A 429 Too Many Requests no longer ends a long fetch or sync on the spot. The page is retried up to three times after the wait Retry-After asks for (seconds or an HTTP date, each wait capped at one minute), or one, two and four seconds without it, and one command waits at most three minutes on 429 answers in total; only then is it an API_ERROR, with a hint that says how often the page was retried, how long the command waited, and to try again later or ask for a shorter range. A transport failure (DNS, connection refused) is an API_ERROR with a hint too, where it was an UNKNOWN with none. On a terminal, fetch and sync show a one-line progress counter on stderr (fetching heartrate: page 41, 40000 rows so far…), which also says when the client is waiting on a 429 (; rate limited, retrying in 30 s…) and is wiped when the command finishes; a 61-day fetch hr is 77 pages and was over a minute of silence, and a rate-limit wait was the same silence. On a pipe stderr stays clean, since it carries the error envelope. doctor names a next step when the API could not be reached, where it reported ok: false with nextStep: null. No upper bound was put on the range: a long backfill is a legitimate use, and pacing rather than refusing is what the API asks for. (#45)
  • A malformed response body from the Oura API is now an API_ERROR (exit 3) naming the endpoint and what came back, instead of an UNKNOWN with an internal expression. sync and fetch walked whatever sat under data: an object failed with {} is not iterable, and a string was iterated character by character and reported as dropped samples that never existed. A missing or null data is still an empty page; a next_token that is not a string or null is the same error, not a last page. (#112)
  • db date and db today mark a day whose activity totals are not final the way db week and report already do: 2026-09-10* in the header and one note under the numbers. The JSON has carried partial since 0.7.1; the table output did not show it, so drilling from a marked week row into the day lost the mark. The note's wording is now one string shared by the day and week views. (#113)
  • Small text inconsistencies from the eight-collection expansion. db stats names each table by both its collection name and its table (sleep-periods (sleep_model)), as the sync lines already did, and prints 1 row, not 1 rows; its JSON rows carry a collection field. db stats explains itself when the cache holds rows but no daily summaries, instead of printing counts and nothing else. The * note in report says "activity totals are not final" like the day and week views, which reads right when the marked day is in the past. sync --help describes what the command prints; the unknown-flag hint no longer claims there are no single-letter flags when -v and -h exist; README names the identity fields the per-collection schemas actually pin. (#72)
  • A sync response that includes a row from outside the window it was asked for no longer widens that request's reconciliation scope. The scope of one request was taken from the timestamps that came back, so a single early or empty-string timestamp reached back over every stored sample between it and the window: on a 60-sample cache one stray value put 59 rows up as "not returned", with the truncation guard's own message naming the --prune=hr flag that would have deleted them. Each request now carries the range it asked for and only rows inside it count towards the scope; rows outside are still stored, they just vouch for nothing. (#111)