AphrodyBun GitHub

Runtime docs · Package Manager · Advanced Configuration

System packages (winget)

Declare winget packages in package.json and install them with bun install

bun install can install system packages next to npm packages. You list them under systemDependencies in package.json, keyed as <source>:<id>. Bun resolves them, records them in bun.lock, and installs them. It reads winget's index directly and never calls winget.exe.

package.json
{
  "name": "my-app",
  "systemDependencies": {
    "winget:jqlang.jq": "*",
    "winget:Microsoft.PowerToys": "0.100.0"
  }
}

Adding and removing

bun add winget:jqlang.jq
bun add winget:Microsoft.PowerToys@0.100.0
bun remove winget:jqlang.jq

A specifier is <source>:<id>[@<range>]. If you leave out the range, Bun writes "*". Package ids are case-insensitive. The key written to package.json is the one you typed.

Ranges compare version components numerically, the way winget does. Supported ranges are *, an exact version, 1.2.x, >=, >, <=, <, ^, ~ and alternatives joined with ||.

How resolution works

Bun reads the winget community source the same way winget does:

  1. It downloads source2.msix and reads Public/index.db with Bun's built-in SQLite reader. The index is cached for one hour.
  2. It downloads packages/<id>/<hash>/versionData.mszyml and decompresses the list of versions.
  3. It downloads the manifest for the chosen version and checks it against the sha256 listed in the version data.

The result is pinned in the "system" block of bun.lock (or bun.lockb). Each entry records the version, the manifest URL and the manifest's sha256. bun install --frozen-lockfile fails when systemDependencies no longer matches the lockfile.

Installing

On Windows, bun install picks the installer that best matches the host architecture, the requested scope and the locale. Before running anything, it checks the installer against the manifest's InstallerSha256.

Installer typeWhat Bun does
portable, zipExtracts into node_modules/.system/winget/<id> and creates node_modules/.bin/<command> shims
msi, wix, burn, inno, nullsoftRuns the installer silently with the manifest's switches
exeRuns the installer only if the manifest provides a Silent switch
msix, appxRuns Add-AppxPackage

Some installers need administrator rights: those with ElevationRequirement, machine-scoped ones, and msi, inno or burn installers with no scope. Bun refuses to run these unless the shell is already elevated or BUN_SYSTEM_ELEVATE=1 is set.

Bun records what it installed in node_modules/.system/.bun-system/installed.json. When an entry leaves the lockfile, bun install uninstalls it: msiexec /x for msi packages, Remove-AppxPackage for msix packages, and the registered uninstall command for the others.

On other platforms, winget packages are still resolved and locked, but installing them is skipped with a warning. A lockfile created on Linux or macOS therefore works unchanged on Windows.

Environment variables

VariableEffect
BUN_WINGET_SOURCEBase URL of the winget source. Default: https://cdn.winget.microsoft.com/cache
BUN_SYSTEM_ROOTInstall into this directory instead of node_modules/.system. Shims go to <root>/bin
BUN_SYSTEM_ELEVATESet to 1 to allow installers that need administrator rights
BUN_SYSTEM_ARCHOverride the host architecture: x64, arm64 or x86
BUN_SYSTEM_SCOPEPrefer user or machine installers
BUN_SYSTEM_LOCALEPreferred installer locale, for example en-US
BUN_SYSTEM_REFRESHSet to 1 to download the index again even if the cached copy is less than an hour old

Downloaded indexes, manifests and installers are cached under <install cache>/system/winget.