The only hard prerequisites are curl and bash, both present on any macOS or Linux system. The bootstrap script downloads everything else.
If you already have Node.js 22+ and uv on your PATH, you can skip the bootstrap and use them directly.
./bootstrap.sh # downloads Node.js 22, uv, prek into .build-tools/
source .build-tools/env.sh # puts them on PATH
npm install # install dependencies
prek install && prek install -t commit-msg # install git hooks
node build.js # full build (SEA + VSIX + zip)bootstrap.sh downloads three tools into .build-tools/ (gitignored):
| Tool | Source | Why |
|---|---|---|
Node.js (version from .node-version) |
nodejs.org official tarball | Official binaries include the NODE_SEA_FUSE sentinel required for SEA injection. Package-manager Node (Homebrew, apt, nvm) usually does not. |
| uv | astral.sh | Python toolchain manager. Used to build the Python client wheel via uvx hatch build without needing a system Python. |
| prek | github.com/j178/prek | Pre-commit hook framework (single Rust binary). Enforces conventional commits and lint checks at commit time. |
After running the bootstrap, source the generated env file to put the tools on PATH:
source .build-tools/env.shThe bootstrap is idempotent -- re-running it is a no-op if the tools are already downloaded.
The bootstrap auto-detects your OS and architecture:
| OS | Architecture | Node.js archive | Tested in CI |
|---|---|---|---|
| Linux | x86_64 (x64) | node-v22.x.x-linux-x64.tar.xz |
Yes |
| Linux | aarch64 (arm64) | node-v22.x.x-linux-arm64.tar.xz |
Yes |
| macOS | arm64 (Apple Silicon) | node-v22.x.x-darwin-arm64.tar.xz |
Yes |
| macOS | x86_64 (Intel) | node-v22.x.x-darwin-x64.tar.xz |
No (not in CI matrix) |
| Windows | x86_64 (x64) | node-v22.x.x-win-x64.zip |
Yes |
On Windows, run ./bootstrap.sh from Git Bash (same as GitHub Actions
windows-latest). Local IPC uses loopback TCP and %TEMP%/abbenay/daemon.addr
(see DR-044); Unix platforms keep daemon.sock.
The Node.js version is stored in .node-version at the repo root. This file is read by bootstrap.sh and is also compatible with nvm, fnm, volta, and mise.
node build.jsThis runs 6 stages:
- Generate Python gRPC client (requires
python3-- skips if missing) - Generate TypeScript gRPC client (requires
protoc-- skips if missing) - Build daemon: tsc type-check, esbuild bundle, core package, SEA binary
- Package VS Code extension (VSIX)
- Create distribution zip
- Install VSIX into VS Code (only with
--code-install)
| Flag | Effect |
|---|---|
--skip-proto |
Skip proto generation (use committed stubs) |
--proto-only |
Only regenerate proto stubs, then stop |
--skip-zip |
Skip creating distribution zip |
--code-install |
Build + install VSIX into VS Code |
The daemon is packaged as a Single Executable Application. The build copies the Node.js binary, injects the bundled JS via postject, and produces a self-contained abbenay-daemon-{platform}-{arch} binary.
The SEA build runs a preflight check at the start -- before doing any esbuild work -- to verify the Node.js binary has the NODE_SEA_FUSE sentinel and that postject is available. If either is missing, the build fails immediately with a clear error.
When using the bootstrap, the downloaded Node.js is always the official binary, so the fuse is always present. If you're using your own Node.js, you can override with:
NODE_SEA_BASE=/path/to/official/node node build.jsnpm run ci:package-pythonThis runs uvx hatch build in packages/python/, producing a wheel in packages/python/dist/. The bootstrapped uv handles downloading hatch and any needed Python version automatically.
The build handles macOS-specific SEA requirements automatically: it passes --macho-segment-name NODE_SEA to postject and re-signs the binary with an ad-hoc signature (codesign --sign -). No manual steps needed.
| Script | Command | Purpose |
|---|---|---|
build |
node build.js |
Full build |
build:dev |
node build.js --skip-zip --code-install |
Build + install VSIX, no zip |
build:proto |
node build.js --proto-only |
Regenerate proto stubs only |
lint |
npm run lint --workspaces --if-present |
Lint all packages |
check:no-query-secrets |
npm run check:no-query-secrets |
Ban query-string API key patterns in sources (DR-035) |
test |
npm run test --workspaces --if-present |
Test all packages |
ci:build |
node build.js --skip-proto |
Full build, skip proto (stubs committed) |
ci:package-python |
cd packages/python && uvx hatch build |
Build Python wheel |
| Script | Command | Purpose |
|---|---|---|
build |
tsc |
TypeScript compilation |
build:sea |
node build.js |
SEA binary build |
dev |
tsx src/daemon/index.ts |
Run daemon in dev mode (no compile) |
test |
vitest |
Run tests |
# Development (via tsx, no compile step)
cd packages/daemon
npm run daemon # start daemon (foreground)
npm run web # start web dashboard
npm run status # check status
# Production (compiled SEA binary)
abbenay daemon # start daemon
abbenay web # start web dashboard
abbenay status # check status
abbenay list-engines # show all supported engines (sorted, formatted)
abbenay list-models # show configured models from your config
abbenay list-models --discover ollama # query an engine for available models
abbenay chat -m openai/gpt-4o # interactive chat
aby daemon # short alias
# TCP gRPC (optional) — loopback plaintext OK; non-loopback needs TLS or --insecure
# Non-loopback also requires a consumers section (or --allow-open-auth / --insecure)
abbenay daemon --grpc-port 50051
abbenay daemon --grpc-port 50051 --grpc-host 0.0.0.0 --grpc-tls # needs consumers in config
abbenay daemon --grpc-port 50051 --grpc-host 0.0.0.0 --insecure # not recommended
abbenay daemon --grpc-port 50051 --grpc-host 0.0.0.0 --grpc-tls --allow-open-auth # not recommended| Flag | Default | Notes |
|---|---|---|
--grpc-port <port> |
(off) | Also listen for gRPC on TCP |
--grpc-host <host> |
127.0.0.1 |
Bind address; 0.0.0.0 for containers |
--grpc-tls |
off | Enable TLS (auto-generated self-signed certs) |
--insecure |
off | Allow plaintext on non-loopback binds; also permits empty consumers |
--allow-open-auth |
off | Allow empty consumers on non-loopback binds (prefer configuring consumers) |
Non-loopback binds without a consumers section fail closed unless
--allow-open-auth or --insecure is set. See
CONFIGURATION.md for
the capability model and
CONTAINER.md for
client trust and insecure tradeoffs.
| Command | What it shows | Network? |
|---|---|---|
list-engines |
All 20 supported engines with auth, tool support, and base URL | No |
list-models |
Configured provider/model pairs from your config (usable with chat -m) |
No |
list-models --discover <engine> |
All models available from an engine's API | Yes |
All list commands support --json for machine-readable output.
Edit src/core/ or src/daemon/, then:
cd packages/daemon
npm run daemon # tsx runs directly, no build needed- Edit
proto/abbenay/v1/service.proto - Regenerate TypeScript stubs:
node build.js --proto-only - The daemon uses
@grpc/proto-loaderfor dynamic loading and does not need regeneration
- Open
packages/vscodein VS Code - Press F5 to launch Extension Development Host
- Check Output panel -> "Abbenay Provider" for logs
The extension has two webviews built with @vscode-elements/elements (Lit-based web components for native VS Code look):
| Webview | Type | Entry point | Handler |
|---|---|---|---|
| Provider Configuration | Editor panel | src/webview-ui/provider/main.ts |
src/webviews/provider/providerHandler.ts |
| Chat Sidebar | Activity bar view | src/webview-ui/chat/main.ts |
src/webviews/chat/chatHandler.ts |
Webview UI lives in src/webview-ui/ (bundled by esbuild, excluded from tsc). Extension host handlers live in src/webviews/.
cd packages/vscode
node esbuild.js # dev build (sourcemaps)
node esbuild.js --production # production build (minified)
node esbuild.js --watch # watch modeThis builds both the extension host (out/extension.js) and webview bundles (out/webview-ui/*/main.js).
- Edit
packages/daemon/static/index.html - Restart the web server to see changes
CI runs in GitHub Actions (.github/workflows/ci.yml). The workflow follows a lean CI philosophy: GitHub Actions is a thin wrapper that calls the same scripts developers run locally.
lint-and-test (ubuntu-latest)
└─ ./bootstrap.sh → npm ci → npm run lint
└─ apt install xvfb → xvfb-run -a npm test
build (matrix: linux-x64, linux-arm64, macos-arm64, windows-x64)
└─ ./bootstrap.sh → npm ci → npm run ci:build
└─ Windows also runs npm run ci:smoke-win32-ipc
└─ uploads: SEA binary, VSIX, distribution archive (.tar.gz or .zip)
package-python (ubuntu-latest)
└─ ./bootstrap.sh → npm run ci:package-python
└─ uploads: Python wheel
bootstrap.sh detects the $GITHUB_PATH environment variable (set by GitHub Actions) and automatically appends its PATH entries there, so all subsequent workflow steps have node, npm, uv, uvx, and prek available without re-sourcing.
Every CI run produces downloadable artifacts:
| Artifact | Contents |
|---|---|
abbenay-daemon-linux-x64 |
SEA binary + sidecars (Linux x64) |
abbenay-daemon-linux-arm64 |
SEA binary + sidecars (Linux arm64) |
abbenay-daemon-darwin-arm64 |
SEA binary + sidecars (macOS Apple Silicon) |
abbenay-daemon-win32-x64 |
SEA binary (.exe) + sidecars (Windows x64) |
abbenay-vsix-{platform}-{arch} |
VS Code extension (per platform) |
abbenay-client-python |
Python wheel (platform-independent) |
A separate workflow (.github/workflows/release.yml) triggers when you push a v* tag. It builds all platforms and creates a GitHub Release with the artifacts permanently attached.
You can create a release from the GitHub UI (recommended) or from the CLI:
git tag v2026.3.1-alpha
git push --tagsTags containing alpha, beta, or rc are automatically marked as prereleases. The workflow uses CalVer (vYYYY.M.MICRO[-prerelease]); do not use leading zeros in the month (semver prohibits them).
Each release produces these artifacts:
| Artifact | Description | Who needs it |
|---|---|---|
abbenay-VERSION-linux-x64.tar.gz |
Standalone daemon binary + sidecars (proto, static, keytar) for Linux x64 | Standalone / CLI users on Linux x64 |
abbenay-VERSION-linux-arm64.tar.gz |
Same, for Linux arm64 | Standalone / CLI users on Linux arm64 |
abbenay-VERSION-darwin-arm64.tar.gz |
Same, for macOS Apple Silicon | Standalone / CLI users on macOS |
abbenay-VERSION-win32-x64.zip |
Same, for Windows x64 (abbenay-daemon-win32-x64.exe) |
Standalone / CLI users on Windows |
abbenay-provider-linux-x64-VERSION.vsix |
VS Code extension with embedded daemon (Linux x64) | VS Code users on Linux x64 |
abbenay-provider-linux-arm64-VERSION.vsix |
Same, for Linux arm64 | VS Code users on Linux arm64 |
abbenay-provider-darwin-arm64-VERSION.vsix |
Same, for macOS arm64 | VS Code users on macOS |
abbenay-provider-win32-x64-VERSION.vsix |
Same, for Windows x64 | VS Code users on Windows |
abbenay-core-VERSION.tgz |
@abbenay/core npm package (platform-independent) |
Node.js consumers building on the core library |
abbenay_client-VERSION-py3-none-any.whl |
Python gRPC client wheel (platform-independent) | Python consumers of the gRPC API |
abbenay_client-VERSION.tar.gz |
Python client sdist | Alternative to the wheel |
Quick guide:
- VS Code user -- download the
.vsixmatching your OS/arch, thencode --install-extension <file>. The daemon is bundled inside. - CLI / standalone daemon -- download the
.tar.gzfor your platform, extract, and run./abbenay-daemon. - Node.js library --
npm install abbenay-core-*.tgz. - Python gRPC client --
pip install abbenay_client-*.whl.
Every CI step is a standard npm script. To reproduce a CI build on your machine:
./bootstrap.sh
source .build-tools/env.sh
npm ci
npm run lint
npm test # or: xvfb-run -a npm test (headless Linux)
npm run ci:build
npm run ci:package-pythonEdit packages/daemon/src/core/engines.ts and add a new entry to the ENGINES record. No other code changes needed.
For a dedicated @ai-sdk/* provider:
newengine: {
id: 'newengine',
requiresKey: true,
defaultBaseUrl: 'https://api.newengine.com/v1',
defaultEnvVar: 'NEWENGINE_API_KEY',
supportsTools: true,
createModel: (modelId, config) =>
dedicatedProvider('@ai-sdk/newengine', 'createNewEngine', config, modelId),
},For an OpenAI-compatible provider:
newcompat: {
id: 'newcompat',
requiresKey: true,
defaultBaseUrl: 'https://api.newcompat.com/v1',
defaultEnvVar: 'NEWCOMPAT_API_KEY',
supportsTools: true,
createModel: (modelId, config) =>
openaiCompatibleProvider('newcompat', 'https://api.newcompat.com/v1', config, modelId),
},- Edit
proto/abbenay/v1/service.proto - Regenerate stubs:
node build.js --proto-only - Implement handler in
packages/daemon/src/daemon/server/abbenay-service.ts
cd packages/daemon
npm test # all tests
npx vitest run src/ # unit tests only
npx vitest run tests/ # integration tests onlyUse mock/echo, mock/fixed, mock/error engines for testing without network or API keys.
The extension uses @vscode/test-cli with @vscode/test-electron to run tests inside a real VS Code instance. Configuration is in packages/vscode/.vscode-test.mjs.
cd packages/vscode
npm test # compiles (pretest) then runs vscode-testOn headless Linux (no display server), wrap with xvfb-run:
xvfb-run -a npm testFrom the repo root, npm test runs both daemon and extension tests via workspaces.
After bootstrapping, install git hooks once:
prek install
prek install -t commit-msgThis installs two hooks from .pre-commit-config.yaml:
- commit-msg: validates commit messages against Conventional Commits via commitlint
- pre-commit: runs
npm run lint
To run hooks manually without committing:
prek run --all-files # run all pre-commit hooks
prek run commitlint # run just the commitlint hook- Daemon logs: console output from the daemon process
- Socket check:
ls -la /run/user/$(id -u)/abbenay/ - VS Code logs: Output panel -> "Abbenay Provider"
pkill -f "abbenay-daemon"
rm -f /run/user/$(id -u)/abbenay/daemon.sock
npm run daemonnode build.js --proto-only # regenerate stubs
cd packages/daemon && npm run build- Ensure daemon is running:
npm run status(inpackages/daemon) - Check Output panel for connection errors
- Reload VS Code window