-v raises the CLI's log level per repeat; -q prints only fatal errors (closes #212)
check / check (push) Waiting to run

Every line quak prints on stderr now goes through one log, in
src/cli-log.ts, at one of six levels. The default, info, prints what quak
printed before. -v adds each retry and the start and end of each refresh
and ML data fetch; -vv adds each request with its result. -q prints only
the line a failing command exits with, and is refused with -v. Both are
program options. withRetry reports retries through a new onRetry, and
ApiClient reports requests through a new onRequest; the CLI logs what
they report. --json now changes stdout only, so backup and --json runs
log their progress on stderr.

Model: opus-5-5
This commit is contained in:
2026-10-18 18:36:33 +00:00
parent 92e0c6c791
commit f9969b2525
17 changed files with 876 additions and 155 deletions
+52 -11
View File
@@ -378,6 +378,8 @@ quak/
thumbnails.ts detect + regenerate missing thumbnails
client.ts high-level Client class assembled from the above
cli-commands.ts the CLI's commands as functions returning exit codes
cli-log.ts the CLI's log level, and the one function the
commands write stderr lines through
cli-output.ts how the CLI prints a file's title and time
cli-read.ts fresh reads for the CLI's read commands
cli-run.ts run a command, print its error, exit with its code
@@ -456,6 +458,12 @@ A custom API endpoint is configurable for self-hosted servers via the
constructor option `apiOrigin`. When set, file downloads route through
`<apiOrigin>/files/download/<id>` instead of the dedicated CDN host.
The constructor option `onRequest` is called once for each request sent, a
retried request once for each attempt, with its method, its URL and the status
of the answer or the error the request failed with. The URL of a presigned
upload is given without its query, which holds the upload's signature. The CLI
logs each request with `-vv` (see "CLI surface").
Required request headers on every authenticated call:
- `X-Auth-Token`: the decrypted auth token from login.
@@ -532,7 +540,9 @@ each wait is drawn at random below its ceiling. Every other command uses the
defaults. A library user gets the same budget by passing
`UNATTENDED_RETRY_OPTIONS` as `ApiClientOptions.retry`. `sleep` and `random` are
injectable through the same option, which is how the test suite exercises the
whole policy without waiting.
whole policy without waiting. `onRetry`, given there too, is called before each
wait with the error the attempt failed with, the attempt's number and the wait;
the CLI logs it with `-v` (see "CLI surface").
Two deadlines, renewed for each attempt:
@@ -646,6 +656,7 @@ thumbnails), for the user to delete if they want it gone.
```
quak [--cache-dir <path>] <command> global: local metadata/content cache location
quak [-v | -vv | -q] <command> global: log more on stderr, or only fatal errors
quak login interactive or QUAK_EMAIL/QUAK_PASSWORD
quak whoami print logged-in account as JSON
quak logout end the session, delete it
@@ -669,16 +680,46 @@ request, and neither fetches any collection's files nor changes the cache. The
others wait for a refresh. With an empty cache, as on a first run, that refresh
fetches the files of every collection, one collection after another, and stores
nothing until it has them all, which on a large account takes a while. Meanwhile
the command prints a line on stderr for each collection whose files it has
fetched, such as `Fetched the files of 3 of 120 collections`; with `--json`,
`files` and the two helpers print no such line. stdout holds only the command's
output. If that round-trip fails, the command prints the error on one line and
exits 1, or 3 when the server no longer accepts the saved session (see "Session
handling"). An album or file quak cannot decrypt does not fail it; the command
answers from what did decrypt (see "Default reads vs. fresh reads").
the command logs a line on stderr for each collection whose files it has
fetched, such as `Fetched the files of 3 of 120 collections`, with `--json` too;
`backup` does the same while the refresh it starts with runs. stdout holds only
the command's output. If that round-trip fails, the command prints the error on
one line and exits 1, or 3 when the server no longer accepts the saved session
(see "Session handling"). An album or file quak cannot decrypt does not fail it;
the command answers from what did decrypt (see "Default reads vs. fresh reads").
`--cache-dir` overrides where the cache lives; without it each account gets its
own directory under the per-user cache path.
`-v` (`--verbose`) and `-q` (`--quiet`) set how much quak logs on stderr. Like
`--cache-dir`, they go before or after any command's name. Every line quak
prints on stderr is a log line at one of six levels, from the quietest: fatal,
error, warning, info, debug and trace. A run prints the lines at its own level
and at every quieter one. stdout holds only the command's output, at every
level, and `--json` changes stdout only, so a run with `--json` logs the same
lines on stderr as one without it.
- With neither option the level is info: the lines quak prints as it works, such
as a refresh's progress, the files a backup fetches, its summary and the files
that failed.
- `-v` raises the level to debug, which adds each retry of a request, with why
the attempt failed and how long quak waits before the next, and the start and
end of each refresh and each ML data fetch, with the error of one that failed.
- `-vv` raises it to trace, which adds each request quak sends: its method, its
URL and the status of the answer, or the error the request failed with. The
URL of an upload of a thumbnail is logged without its query, which holds the
upload's signature. More repeats stay at trace, and `--verbose --verbose` is
`-vv`.
- `-q` lowers it to fatal: quak prints only the line a command prints as it
fails and exits non-zero, such as `quak: <message>`, `Not logged in`, an
invalid ID or a collection that does not exist. A file that fails inside
`backup` is not fatal: it is in `failures.json` and in the exit code, and
nothing is printed for it. The prompts of `login` are not log lines, and `-q`
leaves them as they are.
`-q` with `-v` is refused with one line,
`error: option '-q, --quiet' cannot be used with option '-v, --verbose'`, and
exit status 1.
`files <collectionID>` lists the files in that collection. `files` with no
argument lists every file in the account, collection by collection, each file
once: a file in several collections is listed with the first of them, which is
@@ -828,8 +869,8 @@ reads vs. fresh reads") goes into `failures.json` with an error that starts
`could not be decrypted:` and gives the reason. If a copy of it in another album
decrypted, it does not: its original is stored from that copy, and the album
where it does not decrypt neither links nor lists it. An album the library could
not decrypt has no name quak can read and no files it can list. A run without
`--json` prints one line for it,
not decrypt has no name quak can read and no files it can list. `quak backup`
prints one line for it on stderr,
`FAILED album <id>: could not be decrypted: <reason>`, and `--json` and the
`BackupResult` list its ID in `undecryptableAlbums`. Such a file in
`failures.json`, or such an album, keeps the exit code at 1, run after run,
@@ -883,7 +924,7 @@ appears or goes away.
A collection whose directory or JSON cannot be written, such as one whose
directory name is taken by a file you put in `collections/`, does not stop the
run. A run without `--json` prints `FAILED album <name>: <reason>` for it,
run. `quak backup` prints `FAILED album <name>: <reason>` for it on stderr,
`--json` and the `BackupResult` list its ID in `failedAlbums`, and the exit code
is 1. The other collections and `failures.json` are still written.