Author SHA1 Message Date
sneak 63ba259ced Node 22.13 minimum, and the library linked to its clone (closes #239)
check / check (push) Successful in 3m17s
`engines.node` and the README now state the minimum that quak's
`yarn.lock` needs: Node 22.13, or 23.5 on Node 23. `@inquirer/prompts`
and its packages refuse 23.0 to 23.4.

"Using the library" now links the clone with `yarn link` instead of
`yarn add file:../quak`. A linked quak runs on the dependencies the clone
installed from `yarn.lock`; `file:` resolved them afresh, so a new
upstream release could raise the Node minimum without any change here.

Model: opus-5-5
2026-11-11 21:02:11 +00:00
clawbot 8e120a4f49 Remove the no-op --collection option from get and get-thumb (closes #241)
check / check (push) Successful in 3m6s
Both commands listed `--collection <id>` as accepted and ignored. It now
is refused as an unknown option, and the README no longer mentions it.

Model: opus-5-5
2026-11-11 21:59:55 +01:00
6 changed files with 46 additions and 33 deletions

No files matched your search

+1 -13
View File
@@ -28,20 +28,8 @@ build:
# Bundles the built dist/, so the binary reports the version script/build
# stamped.
#
# On Linux, nix's bun loads through a C library in the nix store, and a binary
# bun compiles from itself keeps that loader. nix-collect-garbage can delete
# that C library, and the binary then no longer starts.
# A --target other than the host's makes bun use its downloaded release build,
# which loads through the system's C library. That is tested only on x86-64,
# with the baseline build, so other Linux machines are refused.
build-bin: build
case "$$(uname -s)-$$(uname -m)" in \
Linux-x86_64) target=--target=bun-linux-x64-baseline ;; \
Linux-*) echo "make build-bin: on Linux only x86-64 is supported" >&2; exit 1 ;; \
*) target= ;; \
esac; \
nix-shell -p bun --run "bun build dist/bin/quak.js --compile $$target --outfile bin/quak"
nix-shell -p bun --run "bun build dist/bin/quak.js --compile --outfile bin/quak"
install: build-bin
mkdir -p ~/bin
+28 -13
View File
@@ -31,7 +31,8 @@ again on the next run. Every download is decrypted on its way to disk.
## Getting Started
The commands below need `git`, Node 22 or later and `yarn`.
The commands below need `git`, `yarn` and Node 22.13 or later. Node 23 needs
23.5 or later.
```bash
git clone https://git.eeqj.de/sneak/quak.git
@@ -69,11 +70,29 @@ The library keeps a local copy of the account's album and file lists, the cache.
It brings the cache up to date with the server in rounds called refreshes.
quak is not published to a package registry: `package.json` marks it `private`,
and `dist/` is not committed. Clone and build it as above, then add the clone to
your own program's `package.json` by its path, for example
`yarn add file:../quak`. The package's `main`, `types` and `exports` fields
point into `dist/`, so the clone must be built first. The package is an ES
module only, so a CommonJS program cannot `require` it.
and `dist/` is not committed. Clone and build it as above, then link the clone
into your own program:
```bash
# In the quak clone:
yarn link
# In your program's directory:
yarn link quak
```
`yarn link` also installs the `quak` command in yarn's global bin directory, and
`yarn unlink` in the clone removes the link and the command.
A linked quak uses the dependencies that `yarn install` put in the clone, at the
versions in quak's `yarn.lock`. Avoid `yarn add file:../quak`: it resolves
quak's dependencies again, and their newer releases can need a newer Node.
`yarn link quak` does not add quak to your `package.json`, so run it again in
each new checkout of your program.
The package's `main`, `types` and `exports` fields point into `dist/`, so the
clone must be built first. The package is an ES module only, so a CommonJS
program cannot `require` it.
```ts
import { Client, Library } from "quak";
@@ -138,10 +157,7 @@ requests. The "Library reference" below covers both.
one binary and copies it to `~/bin/quak`. Besides what Getting Started needs,
`make install` needs `make` and [nix](https://nixos.org/download). It uses bun,
a JavaScript runtime, to bundle the built `dist/` into one executable. It runs
bun through `nix-shell -p bun`. On Linux it works on x86-64 only. There, the
binary is built from bun's own release build rather than nix's bun, so it does
not need the nix store. The first build downloads that release build into
`~/.bun`.
bun through `nix-shell -p bun`.
Log in once with `~/bin/quak`, as the user the cron job will run as:
@@ -305,7 +321,7 @@ title, with unsafe characters replaced (see "File names from the server"). With
original is kept in the cache as well.
A file ID that no collection holds gives `File <id> not found`, and the exit
status is 1. The option `--collection <id>` is accepted and ignored.
status is 1.
A live photo is a still image and a short video that Ente stores as one file.
`get` writes both, each named after the title with its own extension, as Ente's
@@ -1913,8 +1929,7 @@ These apply to people and to coding agents alike.
`bin/clip-keyword-vectors` installs, by SHA-256 hash;
- Node and yarn, when `script/bootstrap` installs them, by version.
- Not pinned:
- bun, which `make build-bin` takes from `nix-shell -p bun`, and on Linux
the release build of the same bun version that it downloads;
- bun, which `make build-bin` takes from `nix-shell -p bun`;
- `make` and `git`, which `script/bootstrap` installs from the system
package manager when they are missing, as every `Dockerfile` stage does;
- `bash` and `curl`, which `script/bootstrap` installs the same way when it
-2
View File
@@ -123,7 +123,6 @@ program
.description("Download and decrypt a single file")
.argument("<fileID>", "File ID (from `quak files`)")
.option("--out <path>", "Output file path")
.option("--collection <id>", "Accepted for compatibility; ignored")
.action((fileID: string, opts: { out?: string }) =>
run(getCommand(context(), fileID, opts)),
);
@@ -133,7 +132,6 @@ program
.description("Download and decrypt a thumbnail")
.argument("<fileID>", "File ID (from `quak files`)")
.option("--out <path>", "Output file path")
.option("--collection <id>", "Accepted for compatibility; ignored")
.action((fileID: string, opts: { out?: string }) =>
run(getThumbCommand(context(), fileID, opts)),
);
+1 -1
View File
@@ -12,7 +12,7 @@
"private": true,
"type": "module",
"engines": {
"node": ">=22"
"node": "^22.13.0 || >=23.5.0"
},
"main": "./dist/src/index.js",
"types": "./dist/src/index.d.ts",
+14 -2
View File
@@ -1,8 +1,9 @@
/**
* Tests for the retry options `bin/quak.ts` loads the saved session with
* (`quak backup` gets the unattended ones, every other command the default),
* each with the callbacks that log its requests and retries, and for
* `quak files` taking its collection ID as an optional argument.
* each with the callbacks that log its requests and retries, for `quak files`
* taking its collection ID as an optional argument, and for `quak get` and
* `quak get-thumb` refusing `--collection`.
*
* Each test runs `bin/quak.ts`, as the smoke test does, with its session loader
* replaced by one that records the options it is given and reports no session,
@@ -105,3 +106,14 @@ describe("bin/quak.ts files", () => {
expect(await quak("files", "7")).toMatch(/^Not logged in/);
});
});
describe("bin/quak.ts get and get-thumb", () => {
it.each(["get", "get-thumb"])(
"%s refuses --collection as an unknown option",
async (command) => {
expect(await quak(command, "1", "--collection", "2")).toMatch(
/^error: unknown option '--collection'/,
);
},
);
});
+2 -2
View File
@@ -569,14 +569,14 @@ describe("files", () => {
});
describe("get and get-thumb", () => {
it("get finds a file in any album without --collection", async () => {
it("get finds a file in any album", async () => {
const out = join(root, "diagram.png");
expect(await getCommand(context(), "200", { out })).toBe(0);
expect(readFileSync(out)).toEqual(Buffer.alloc(7, 200));
expect(stderr.text).toBe(`7 bytes -> ${out}\n`);
});
it("get-thumb finds a file in any album without --collection", async () => {
it("get-thumb finds a file in any album", async () => {
const out = join(root, "thumb.jpg");
expect(await getThumbCommand(context(), "200", { out })).toBe(0);
expect(readFileSync(out)).toEqual(Buffer.alloc(3, 200));