Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
model/.venv/
model/__pycache__/
dist/
npm/dist/
17 changes: 17 additions & 0 deletions npm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# whittle

Cut your AI agent's token bill. Lose nothing that matters.

This package installs the prebuilt `whittle` binary for your platform (macOS and
Linux, arm64 and amd64). It is the npm distribution of
[github.com/firstops-dev/whittle](https://github.com/firstops-dev/whittle); the
same binaries also ship via `go install` and Homebrew.

```sh
npx @firstops/whittle setup # one command: hook + local daemon + sidecar
# or install globally:
npm install -g @firstops/whittle && whittle setup
```

Full documentation, benchmarks, and the fidelity contract:
[the repository](https://github.com/firstops-dev/whittle).
40 changes: 40 additions & 0 deletions npm/bin/whittle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env node
// npx/npm shim: resolves the platform binary package and execs the real whittle.
// The Go binary is unaware this wrapper exists; go install and brew are
// unaffected alternative channels.
"use strict";
const { spawnSync } = require("child_process");

const SCOPE = "@firstops"; // keep in sync with npm/prepare.sh
const goArch = { x64: "amd64", arm64: "arm64" }[process.arch];
const goOS = { darwin: "darwin", linux: "linux" }[process.platform];

if (!goOS || !goArch) {
console.error(
`whittle: no prebuilt binary for ${process.platform}-${process.arch}.\n` +
`Install from source instead:\n` +
` go install github.com/firstops-dev/whittle/cmd/whittle@latest\n` +
`or download a release: https://github.com/firstops-dev/whittle/releases`
);
process.exit(1);
}

let bin;
try {
bin = require.resolve(`${SCOPE}/whittle-${goOS}-${goArch}/bin/whittle`);
} catch {
console.error(
`whittle: platform package ${SCOPE}/whittle-${goOS}-${goArch} is not installed.\n` +
`This usually means npm skipped optional dependencies. Try:\n` +
` npm install --include=optional\n` +
`or use: go install github.com/firstops-dev/whittle/cmd/whittle@latest`
);
process.exit(1);
}

const r = spawnSync(bin, process.argv.slice(2), { stdio: "inherit" });
if (r.error) {
console.error(`whittle: failed to start binary: ${r.error.message}`);
process.exit(1);
}
process.exit(r.status === null ? 1 : r.status);
18 changes: 18 additions & 0 deletions npm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@firstops/whittle",
"version": "0.0.0-managed-by-prepare-sh",
"description": "Cut your AI agent's token bill. Lose nothing that matters. Write-time tool-output compression and policy-based model routing for Claude Code, local and fail-open.",
"keywords": ["claude", "claude-code", "ai-agents", "context", "compression", "llm", "router", "tokens"],
"license": "Apache-2.0",
"repository": { "type": "git", "url": "git+https://github.com/firstops-dev/whittle.git" },
"homepage": "https://github.com/firstops-dev/whittle#readme",
"bin": { "whittle": "bin/whittle.js" },
"files": ["bin/whittle.js", "README.md"],
"engines": { "node": ">=16" },
"optionalDependencies": {
"@firstops/whittle-darwin-arm64": "0.0.0-managed-by-prepare-sh",
"@firstops/whittle-darwin-amd64": "0.0.0-managed-by-prepare-sh",
"@firstops/whittle-linux-arm64": "0.0.0-managed-by-prepare-sh",
"@firstops/whittle-linux-amd64": "0.0.0-managed-by-prepare-sh"
}
}
51 changes: 51 additions & 0 deletions npm/prepare.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/sh
# prepare.sh <version> [--publish]
# Builds the npm packages for a tagged release: downloads the goreleaser assets,
# verifies checksums, assembles 4 platform packages + the meta package under
# npm/dist/, and (with --publish) publishes all five. Version = the git tag
# without the v prefix, so npm always mirrors a real GitHub release.
set -eu
SCOPE="@firstops" # keep in sync with npm/bin/whittle.js
V="${1:?usage: prepare.sh <version, e.g. 0.3.0> [--publish]}"
PUBLISH="${2:-}"
cd "$(dirname "$0")"

work=$(mktemp -d) && trap 'rm -rf "$work"' EXIT
echo "downloading release v$V assets..."
gh release download "v$V" -R firstops-dev/whittle -p '*.tar.gz' -p checksums.txt -D "$work"
(cd "$work" && grep '\.tar\.gz' checksums.txt | shasum -a 256 -c - >/dev/null) && echo "checksums verified"

rm -rf dist && mkdir -p dist
for plat in darwin_arm64 darwin_amd64 linux_arm64 linux_amd64; do
os="${plat%_*}"; arch="${plat#*_}"
pkg="dist/whittle-$os-$arch"
mkdir -p "$pkg/bin"
tar -xzf "$work/whittle_${V}_${plat}.tar.gz" -C "$pkg/bin" whittle
chmod +x "$pkg/bin/whittle"
cat > "$pkg/package.json" <<EOF
{
"name": "$SCOPE/whittle-$os-$arch",
"version": "$V",
"description": "whittle prebuilt binary for $os/$arch (install $SCOPE/whittle instead)",
"license": "Apache-2.0",
"repository": { "type": "git", "url": "git+https://github.com/firstops-dev/whittle.git" },
"os": ["$os"],
"cpu": ["$([ "$arch" = amd64 ] && echo x64 || echo arm64)"],
"files": ["bin/whittle"]
}
EOF
done

mkdir -p dist/whittle/bin
sed -e "s/0\.0\.0-managed-by-prepare-sh/$V/g" package.json > dist/whittle/package.json
cp bin/whittle.js dist/whittle/bin/whittle.js && chmod +x dist/whittle/bin/whittle.js
cp README.md dist/whittle/README.md
echo "assembled: $(ls dist)"

if [ "$PUBLISH" = "--publish" ]; then
for d in dist/whittle-*; do (cd "$d" && npm publish --access public); done
(cd dist/whittle && npm publish --access public)
echo "published v$V (platform packages first, meta last)"
else
echo "dry run complete. To publish: sh npm/prepare.sh $V --publish (requires npm login)"
fi
Loading