feat(dist): self-contained binary + brew/scoop/curl install paths - #128
Merged
click6067-ship-it merged 1 commit intoSep 14, 2026
Merged
Conversation
…nd curl installs The accuracy work only counts if the tool is installable where its users already are. Today the only path is `npx`, which requires Node; the comparable tool in this space ships prebuilt binaries through brew, scoop, uv, docker and a curl one-liner, and its latest release saw 8,751 downloads in four days. This adds the missing paths without touching the engine. It can be done without touching it because the package already has zero runtime dependencies and one non-relative import (`node:child_process`), so `bun build --compile` wraps the existing code as-is. Porting the engine to Rust or Go was rejected: engine.js is the asset, and a port makes a second copy of the truth that will drift from the first. - scripts/build-binaries.mjs builds seven targets — macOS arm64/x64, Linux x64/arm64 in both glibc and musl, Windows x64 — writes a .sha256 next to each and a manifest. For any target the host can actually execute it runs the binary against the node path on three cases and fails the build unless stdout is byte-identical, because a user seeing different numbers depending on how they installed is the worst failure this product has. - packaging/install.sh detects platform and libc, verifies the published checksum, and installs nothing on any failure. Verified by execution: the happy path installs and runs; an unsupported OS, an unsupported arch, a missing release and a tampered binary each exit 1 with zero files written. - scripts/gen-packaging.mjs derives the Homebrew formula and Scoop manifest from the build manifest rather than hand-written hashes, and throws on a missing target instead of emitting a file that points at nothing. - .github/workflows/binaries.yml runs the engine tests, builds, and attaches the assets on a v* tag. Two limits are stated in the README rather than buried: each binary is 60-94 MB because it embeds its runtime (a Rust equivalent is 5-6 MB), and this is a Homebrew tap, not homebrew-core — core's self-submission bar is 225 stars / 90 forks / 90 watchers and this project has 8 stars. Speed claims stay out of the new surfaces; a test asserts it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase A of closing the distribution gap. Plan:
fitllm-v2/docs/plans/2026-09-14-distribution-parity.md(contract PASS, 8 AC).Why
Accuracy only counts if the tool is installable where its users are. Today:
npxonly, which requires Node. The comparable tool in this space ships through brew, scoop, uv, docker andcurl | sh, and its latest release saw 8,751 downloads in four days.What
No engine change. It works untouched because the package has zero runtime dependencies and exactly one non-relative import (
node:child_process), sobun build --compilewraps the existing code. Porting to Rust/Go was rejected —engine.jsis the asset; a port makes a second copy of the truth that drifts.scripts/build-binaries.mjs.sha256each, manifestpackaging/install.shscripts/gen-packaging.mjs.github/workflows/binaries.ymlv*Verified by execution, not assertion
PATH=/var/empty(nonode, nosh) → exit 0, correct output.npm test292/292 (+6 new), vectors 33/33.v2.16.0 assets are already attached, so the install paths work right now.
Stated limits, not buried
Speed claims stay out of the new surfaces (2026-06-05 sealed decision) — a test asserts it.
🤖 Generated with Claude Code