@forst/cli
v0.8.1
Published
Forst compiler CLI: install the native `forst` binary. Linux, macOS, Windows.
Downloads
1,822
Maintainers
Readme
@forst/cli
Node.js distribution of the Forst compiler. This package ships a small wrapper that downloads and caches the official native forst binary for your platform from GitHub Releases—the same artifacts built with task build:release in the main repository. No local Go toolchain is required.
Use it in npm scripts, developer tooling, or as a dependency of libraries such as @forst/sidecar.
Table of contents
- Overview
- What’s included
- Requirements
- Installation
- Command-line usage
- Environment variables
- How the wrapper works
- Programmatic API
@forst/cli/invoke- Releases and publishing
- Security
- Upgrading
- Troubleshooting
- Relationship to
@forst/sidecar - Native compiler CLI
- Support
- License
Overview
@forst/cli bridges JavaScript/TypeScript workflows and the Forst compiler: by default the pinned forst.compilerRelease in package.json (falling back to the npm package semver when unset) determines which native binary is downloaded from GitHub Releases. Callers may opt into preferLatestRelease so resolution uses semantic versioning to pick the newer of the bundled pin and GitHub’s latest release. The binary is cached per version so repeat invocations avoid redundant downloads.
The wrapper is responsible for resolution, verification, and concurrency-safe installation—not for implementing compiler features (those live in the native forst executable).
What’s included
| Deliverable | Description |
| --- | --- |
| forst on PATH | Via node_modules/.bin/forst when installed as a dependency, or npx @forst/cli / npx forst without a global install. |
| JavaScript API | resolveForstBinary, spawnForst, getCliPackageVersion, etc., for tools that need to locate or run the binary programmatically. |
| @forst/cli/invoke | startForstInvokeServer for Node→Forst HTTP invoke lifecycle in tests and globalSetup. |
| @forst/errors (transitive) | Shared invoke/harness failure classes. Installed with @forst/cli; import from @forst/errors (Promise mode, no Effect peer). Effect mode stays at @forst/errors/effect when you add the effect peer. |
| Diagnostics | npx forst --forst-cli-info prints npm semver, pinned forst.compilerRelease, resolved compiler version, binary path, and forst version output—use this in bug reports and CI logs. |
Requirements
| Requirement | Details |
| --- | --- |
| Node.js | Version 18 or later (engines in package.json). |
| Network | HTTPS access to github.com on first use (binary download and release metadata), unless you bypass download with FORST_BINARY. |
| Platforms | Linux, macOS, and Windows on CPU architectures published for your package version on the project’s GitHub Releases. |
Installation
As a dev dependency (typical for apps and libraries):
npm install -D @forst/cliOne-off invocation without adding to package.json:
npx @forst/cli versionAfter install, the wrapper is available as node_modules/.bin/forst.
On first use, the wrapper downloads the binary for the installed package version from GitHub. For offline or locked-down CI, set FORST_BINARY to a pre-installed executable (see Environment variables).
Command-line usage
The wrapper forwards arguments to the native forst binary once it is resolved. Examples:
# After local install
npx forst version
npx forst dev --help
npx forst generateIn package.json scripts:
{
"scripts": {
"forst": "forst",
"dev:forst": "forst dev"
}
}For full command coverage (dev, generate, lsp, fmt, …), see Native compiler CLI.
Environment variables
| Variable | Purpose |
| --- | --- |
| FORST_BINARY | Absolute path to a forst executable; skips download entirely. |
| FORST_CACHE_DIR | Base directory for cached binaries. Default: ~/.cache/forst-cli on Unix, %LOCALAPPDATA%/forst-cli/cache on Windows. Each compiler version is stored in a subdirectory. |
| FORST_CLI_VERIFY | Default (unset): verify SHA-256 when GitHub release metadata includes a digest; skip verification and download anyway when no digest is available. Set 0 or false to never verify. Set 1 or strict to require a digest and refuse unverified downloads. |
| FORST_BASE_URL / FORST_INVOKE_URL / FORST_DEV_URL | Used by @forst/cli/invoke to attach to an existing invoke server instead of spawning. |
| FORST_SKIP_SPAWN | When 1 or true, @forst/cli/invoke must attach (via ready file or URL env). Never spawns. |
| FORST_CLI_INVOKE_E2E | Set to 1 to run the opt-in real spawn test in this package. |
Behavior: downloads retry on transient HTTP errors, use an exclusive lock when two processes install concurrently, and write the binary atomically so a partial file never replaces a good one. In air-gapped or API-restricted environments, prefer FORST_BINARY. Use FORST_CLI_VERIFY=strict only when you must refuse downloads without a digest.
How the wrapper works
By default, programmatic resolution (resolveForstBinary with no options) follows the same rules as the CLI shim:
- Read the pinned
forst.compilerReleasefrompackage.json(falls back to the npm package semver when unset). - Resolve the corresponding release assets on GitHub for that version.
- Download (unless
FORST_BINARYis set), verify when a digest is available (unlessFORST_CLI_VERIFYdisables verification), and place the binary in the cache. - Execute the native
forstwith your arguments.
Optional: follow GitHub’s latest compiler release (semantic versioning). Pass preferLatestRelease: true to resolveForstBinary (and keep allowDownload: true). The wrapper scans GitHub releases for root compiler tags (vX.Y.Z), compares the highest tag to the bundled forst.compilerRelease, and uses the higher semver for the cache path and download. The response is cached on disk under your cache root (.latest-compiler-release.json, refreshed about once per hour). If the latest check fails (offline, rate limits), resolution falls back to the bundled compiler release only.
Upgrading the npm package remains the supported way to move the minimum compiler version in Node workflows; preferLatestRelease is for integrations (for example the VS Code extension) that should track GitHub releases between extension publishes.
Programmatic API
import { resolveForstBinary, spawnForst, getCliPackageVersion } from "@forst/cli";
const bin = await resolveForstBinary();
// Optional: max(bundled semver, GitHub latest) — see "How the wrapper works" above.
// await resolveForstBinary({ preferLatestRelease: true });
// spawnForst(["generate", "-h"], {}, { version: "0.0.19" });See TypeScript definitions under dist/ after build, or source in src/.
@forst/cli/invoke
Starts, attaches to, and stops a Forst HTTP invoke server (POST /invoke). This is the Node→Forst direction. It is orthogonal to @forst/node-runtime, which is Forst→Node RPC (startForstNodeHost).
Typical use is integration tests and globalSetup. Application code that uses a generated client usually imports helpers from @forst/gen/$testing instead; those helpers call this subpath.
import { startForstInvokeServer } from "@forst/cli/invoke";
await using server = await startForstInvokeServer({ root: process.cwd() });
// server.baseUrl → http://127.0.0.1:<port>Behaviour in short:
- Attach before spawn when
baseUrl,FORST_SKIP_SPAWN,.forst/invoke.ready, orFORST_BASE_URL/FORST_INVOKE_URL/FORST_DEV_URLis set. Attach handles never kill the process. mode: "auto"(default) readsftconfig.jsonand picksembeddedwhenserver.embeddedornode.hostModeis true, otherwisedev.- Readiness is
GET /health, not log scraping. stop()sendsSIGTERM, thenSIGKILLafter 5s.
For host mode (node.hostMode in ftconfig.json), call prepareInvokeConnect() at process startup so connect-mode env and the FORST_INVOKE_AUTH_RECV_FD listener are ready. Use getInvokeAuthHandoff() with a generated client's resolveAuth when auth arrives over the host pipe instead of FORST_INVOKE_TOKEN.
Opt-in real spawn test (needs a local forst binary and Go for embedded compile):
FORST_CLI_INVOKE_E2E=1 bun test src/test-server.e2e.test.tsReleases and publishing
| Topic | Details |
| --- | --- |
| Versioning | Release Please manages packages/cli with the Go compiler via the linked-versions plugin. Both share the same semver. Tags like cli-v* bump package.json and jsr.json (see .release-please-config.json). The first linked release may jump @forst/cli from its old line (for example 0.6.x) to the current compiler line. |
| Compiler pin | Root compiler releases update forst.compilerRelease via Release Please extra-files in the same release PR that bumps @forst/cli. The npm version and pin stay aligned because the compiler and CLI release together. |
| Automation | When a GitHub Release is published, .github/workflows/publish-packages.yml publishes @forst/cli to npm and JSR. |
| npm | Package page. CI may use trusted publishing (OIDC); a repository NPM_TOKEN can still be used where a classic token fallback is required. |
| JSR | jsr.io/@forst/cli—link the repository for OIDC or configure JSR_TOKEN as documented by JSR. |
| Manual JSR | From packages/cli: npx jsr publish --dry-run or npx jsr publish. A clean git tree is expected unless you pass --allow-dirty. |
| repository.url | For trusted publishing from GitHub Actions, package.json → repository.url must exactly match the GitHub repo running CI (e.g. git+https://github.com/forst-lang/forst.git). Forks must update this field to the fork (or publish only from the upstream repo)—otherwise npm can reject the publish. |
Security
- Integrity: when GitHub exposes a digest on release assets, binaries are verified by default (
FORST_CLI_VERIFYunset). Missing digests skip verification unlessFORST_CLI_VERIFY=strict. - Install safety: atomic writes and locking reduce the risk of corrupted executables under parallel installs.
- Operational hardening: in restricted environments, supply a vetted binary via
FORST_BINARYand prefer OIDC or short-lived credentials in CI over long-lived tokens in logs.
Upgrading
Change the @forst/cli version in package.json (or your lockfile) when you need a compiler release that shipped after your current dependency. Unless you opt into preferLatestRelease in code, the wrapper binds to the bundled forst.compilerRelease pin (not the npm semver alone). Compiler and CLI releases share one semver, so upgrading @forst/cli also picks up the matching compiler pin.
After upgrading, run:
npx forst --forst-cli-infoConfirm that the reported package version, binary path, and forst version match your expectations.
Troubleshooting
| Symptom | What to check |
| --- | --- |
| Download fails or 404 on an asset | A GitHub release must exist for the pinned compiler version’s artifacts. When the pinned release is missing but an older release exists, the wrapper may fall back silently—check npx forst --forst-cli-info for pinned vs resolved version. Unpublished pins or forks may need FORST_BINARY or an upgraded @forst/cli. |
| Wrong OS or architecture | Override with FORST_BINARY for custom builds or unsupported matrices. |
| SHA-256 / verification errors | Checksum mismatch (CompilerBinaryChecksumMismatch): downloaded bytes differ from GitHub metadata — do not run the binary; clear the cache entry and retry, or use FORST_BINARY. May indicate corruption, proxy tampering, or supply-chain attack. Strict mode, no digest (CompilerBinaryDigestUnavailable): FORST_CLI_VERIFY=strict requires a digest but the release tag, asset name, or GitHub digest is missing — check forst.compilerRelease. Default (unset) skips verification when no digest is available and still downloads. |
| Concurrent installs | Locking prevents corruption; different versions use separate cache subdirectories. |
Relationship to @forst/sidecar
@forst/sidecar depends on this package and uses resolveForstBinary so dev-server integration and the CLI share one download and cache implementation.
Native compiler CLI
The native binary implements forst dev, forst generate, forst lsp, forst fmt, and related commands. Implementation entry point: forst/cmd/forst/main.go. Refer to the main repository for the full CLI reference and language documentation.
Support
Report issues and feature requests in the issue tracker. Include npx forst --forst-cli-info output and your OS/architecture when reporting download or verification problems.
License
MIT — see LICENSE.
