Monorepo for Aesthetic.Computer aesthetic.computer

easel: a licence that permits the install, and a tool that keeps itself current master

The licence said all rights reserved, inside a tarball we were about to serve from prompt.ac. Now it grants use — install it, run it commercially, modify your copy — and withholds redistribution: forking it to run your own is use and is fine, handing your copy to someone else is distribution and is not. Aesthetic Computer distributes it, from one URL. A tool installed by a shell script has no package manager behind it, so if it does not look after its own version nobody will. The copy someone installs today keeps telling a model about a piece API that moves next month, and the first symptom is bad advice rather than an error — which is the worst shape a stale install can take, because nothing looks broken. So Easel asks once a day, in the background, and says nothing unless there is news. Not automatic: replacing the tool someone is mid-sentence with is the wrong kind of surprise. `/update` is the verb, and every failure path in the check is silent — no network, a bad shape, a server error all mean "no update today" rather than an error in front of someone trying to draw something. Two rules shape the updater. It never touches a checkout: `install.json` is written into the tarball by pack.mjs and exists nowhere else, so its absence means this is a working copy where overwriting src/ would destroy an afternoon. Development is the case that must never be guessed wrong, so it is detected by a file only a release can have rather than by sniffing for .git and hoping. And it never installs bytes it did not verify: the manifest carries the tarball's sha256, the download is hashed before anything is unpacked, and a mismatch is refused rather than reported, because this is code that will run as the user on their next launch. The swap renames a fully unpacked directory and puts the old one back if that fails, so there is no moment where half an Easel sits at the path a terminal is about to launch. Version comparison is numeric, so 0.10 is newer than 0.9 rather than alphabetically older, and anything unparseable compares equal — every unreadable case fails toward not updating. Found while testing the round trip: bin/easel carried VERSION as a literal, so after a successful self-update the launcher went on reporting the version it was written with while package.json, which is what the updater actually compares, had moved on. It reads package.json now. Two places to change a version is one place to forget. Verified end to end in a throwaway HOME against a local server: install 0.4.0, publish 0.5.0, notice it, download it, verify the checksum, swap, and report 0.5.0 from the launcher — with the repository still on 0.4.0 afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>