From 4868222e3414554987bf4b05fbb393fc65080aa0 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Thu, 21 May 2026 00:21:21 +0200 Subject: [PATCH] chore(tui): replace koffi with Windows VT input helper closes #4480 --- package-lock.json | 14 --- packages/coding-agent/npm-shrinkwrap.json | 14 --- packages/tui/CHANGELOG.md | 4 + .../win32-arm64/win32-console-mode.node | Bin 0 -> 3072 bytes .../win32-x64/win32-console-mode.node | Bin 0 -> 3072 bytes .../tui/native/win32/src/win32-console-mode.c | 53 ++++++++++ packages/tui/package.json | 4 +- packages/tui/src/terminal.ts | 38 ++++--- scripts/build-binaries.sh | 20 ++-- scripts/generate-coding-agent-shrinkwrap.mjs | 1 - scripts/local-release.mjs | 98 ++++++++++++++++-- 11 files changed, 176 insertions(+), 70 deletions(-) create mode 100644 packages/tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node create mode 100644 packages/tui/native/win32/prebuilds/win32-x64/win32-console-mode.node create mode 100644 packages/tui/native/win32/src/win32-console-mode.c diff --git a/package-lock.json b/package-lock.json index fb54cf29c..3e9361400 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3773,17 +3773,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/koffi": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.16.2.tgz", - "integrity": "sha512-owU0MRwv6xkrVqCd+33uw6BaYppkTRXbO/rVdJNI2dvZG0gzyRhYwW25eWtc5pauwK8TGh3AbkFONSezdykfSA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "funding": { - "url": "https://liberapay.com/Koromix" - } - }, "node_modules/lodash-es": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", @@ -6190,9 +6179,6 @@ }, "engines": { "node": ">=22.19.0" - }, - "optionalDependencies": { - "koffi": "2.16.2" } } } diff --git a/packages/coding-agent/npm-shrinkwrap.json b/packages/coding-agent/npm-shrinkwrap.json index 453f236f5..85a22b1a1 100644 --- a/packages/coding-agent/npm-shrinkwrap.json +++ b/packages/coding-agent/npm-shrinkwrap.json @@ -516,9 +516,6 @@ "get-east-asian-width": "1.6.0", "marked": "15.0.12" }, - "optionalDependencies": { - "koffi": "2.16.2" - }, "engines": { "node": ">=22.19.0" } @@ -1369,17 +1366,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/koffi": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.16.2.tgz", - "integrity": "sha512-owU0MRwv6xkrVqCd+33uw6BaYppkTRXbO/rVdJNI2dvZG0gzyRhYwW25eWtc5pauwK8TGh3AbkFONSezdykfSA==", - "license": "MIT", - "optional": true, - "hasInstallScript": true, - "funding": { - "url": "https://liberapay.com/Koromix" - } - }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", diff --git a/packages/tui/CHANGELOG.md b/packages/tui/CHANGELOG.md index 56ea936e4..b1f8a8dcb 100644 --- a/packages/tui/CHANGELOG.md +++ b/packages/tui/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Changed + +- Replaced the optional `koffi` dependency for Windows VT input with a tiny vendored native helper, reducing install size while preserving Shift+Tab handling ([#4480](https://github.com/earendil-works/pi/issues/4480)). + ## [0.75.4] - 2026-05-20 ### Changed diff --git a/packages/tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node b/packages/tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node new file mode 100644 index 0000000000000000000000000000000000000000..42b2c77ccadb03f7ad745fda39583b2e6711d10f GIT binary patch literal 3072 zcmeHIU1%It6#ni^o1_T|tzE22(MeoE#4a^IJ_(&zgB4p1+2BKc*v#%+lhNJTb#}Ja z1Z2UN(t>0}TJ*(MeCRF+5hJ7pCAA8^6x4@2`LOW^^58@5whar?^_)99c2lV$_CX<+ z+&%Yv=V!iq?#;P}4$ngeu!Ux`2`rK1VXEK1{`rQ?eGi=P!{W9}{Y&EDrT)nor)U(s z{IqA~jEv>FdEZD&!z;Ol;~GyrpEPoLTPAvYyW_3YpWOS?SL;WQpN!_4jT5J?D*eRC zD|eWq04pk?k@Dj2^A;QD}s8L@3GaYF!pA zcun>W<#bc3LU3`Ed5T=aph)~#50Pbqcu>q*KkzUIM`JH=Pqc269~!{!HvMmxXUVeH`m&mI^?zp1DHy9w&h#ES*{HM%BQ_X1ex@gD1?5BJAat{yJ@x!Q@OSQ zo6G-v1?tJqD>r)3Xzs_iUBE)u4zp3$Q8h&QE{J_AO&#a1lKy@NKCTH&DxYm;V^v4M zc2i(@O*hZg^jKAhU4zQrYc|+61q=_PJV93_79+_v2oa0`@2pYIW+xgz`Ma-y#UCC- zu&T!%sp&hb99!!&nNx71hxjDFIJ>N;R=Ak;y$_3wM1}T7uEYI z_q7PSAcFfyXa2O0#)yW+7)|n@*9C%|#K#)Yf}d$GTZo5-^4m3iSM{{EZ)F)6`B66; zX|k{CcUKuN6JDb=^ID&Kl6Z_(>dA|W8*{18p~76cL|8-K8}Eg3@i!N`wsPIV{hb#0 zRPnxmAd0!2FV~NA)#ps!a}d>n>+x^L_&}J!ZpkApd8{SPmW;mRa{soo**|ov;H0Ld zpGxQRS!uZ&@Qf!dU#4Cvxf$QdE1S#yMe=v8oU~K)RurV?zYghI>8yOg@%)mNos?eA zajon#ZlT0Yj7HSYdujb8%{cYdcqd!<-Qt%ExMBd@?xMLy`Y$vu(_Ek)*iA9OYmPfK zxF?f$i$r*jdXIQhP|Q3z?G%0Kr3f!&W$MUd9KwU*xtFTG8qbCMG{PR7aJVn|L5GZ1u0dMjw~g2kW83LG_L6M*4Kv1P zG;e$uxOSgxIwz!{D7L#lWoGzxsr*tnO^ zjM}y*i$yv)M)rqCK+Y8R#Mb03$g^V;&y5`%8f?F98@X<3cc21vuRp>Eg8x6>#Y5VG R9l)zNa|aS{ruaWp;CE*fda3{b literal 0 HcmV?d00001 diff --git a/packages/tui/native/win32/prebuilds/win32-x64/win32-console-mode.node b/packages/tui/native/win32/prebuilds/win32-x64/win32-console-mode.node new file mode 100644 index 0000000000000000000000000000000000000000..2c6d86d8725cb88e742a168f0fd9db2e200e4766 GIT binary patch literal 3072 zcmeHIQD_`h6urCK#&qNErmzc93Qp>L1ZkIbH;RHtstJA7t%fA1h`62XjLFFC>@quB zV+4sV5tCshh_w9```-`!sQn7GVhd>vh#J%ng?isadnBG@Sa3 zqt%qEX4`dFnKl%sWh{5L8_C0mjNq4V1aXo(9~{R>Vsy!f4DAALoB#R> zh@}_QUa{n={m+W_6-9VgKi={oxdbjv=^No)L%@h=U-RAjL}v=7cizu^nIT$Sbq72N ze3m)$!!AE}0bu8~*y3s{?)@e@sqf)$ZlD5uh7gF~UG9vk!uv{im;80%G3vSML&7`D zPC>NKDV4KpYau5~9bjS#6Dxq2@V>^bAMmbk@~)lKDcHi>+-$P@DYoWvgR)rs(LKlq zJ|_6UL+$gvSQ=G_%AM4m$W3%|9bSnQO0eRR{{?7g%U)R(<<8E!-1l>)39gnp9x_Do zuKFBk7MNzhGc4Qn(giE{A$6)V=ox_-g0}9+A!cTeqwmzSb}+GlpB4uGcL?vv0zyj-k0m<)xNgb + +#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT +#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 +#endif + +#define NAPI_AUTO_LENGTH ((unsigned long long)-1) + +typedef void* napi_env; +typedef void* napi_value; +typedef void* napi_callback_info; +typedef napi_value (__cdecl *napi_callback)(napi_env, napi_callback_info); +typedef int (__cdecl *napi_create_function_fn)(napi_env, const char*, unsigned long long, napi_callback, void*, napi_value*); +typedef int (__cdecl *napi_set_named_property_fn)(napi_env, napi_value, const char*, napi_value); +typedef int (__cdecl *napi_get_boolean_fn)(napi_env, int, napi_value*); + +static void* node_symbol(const char* name) { + HMODULE module = GetModuleHandleA(0); + void* proc = module ? (void*)GetProcAddress(module, name) : 0; + if (proc) return proc; + + module = GetModuleHandleA("node.dll"); + return module ? (void*)GetProcAddress(module, name) : 0; +} + +static napi_value __cdecl enable_virtual_terminal_input(napi_env env, napi_callback_info info) { + (void)info; + + HANDLE handle = GetStdHandle(STD_INPUT_HANDLE); + DWORD mode = 0; + int enabled = handle != INVALID_HANDLE_VALUE && + GetConsoleMode(handle, &mode) && + SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_INPUT); + + napi_get_boolean_fn napi_get_boolean = (napi_get_boolean_fn)node_symbol("napi_get_boolean"); + napi_value result = 0; + if (napi_get_boolean) napi_get_boolean(env, enabled, &result); + return result; +} + +__declspec(dllexport) napi_value __cdecl napi_register_module_v1(napi_env env, napi_value exports) { + napi_create_function_fn napi_create_function = (napi_create_function_fn)node_symbol("napi_create_function"); + napi_set_named_property_fn napi_set_named_property = (napi_set_named_property_fn)node_symbol("napi_set_named_property"); + + napi_value fn = 0; + if (napi_create_function && + napi_set_named_property && + napi_create_function(env, "enableVirtualTerminalInput", NAPI_AUTO_LENGTH, enable_virtual_terminal_input, 0, &fn) == 0) { + napi_set_named_property(env, exports, "enableVirtualTerminalInput", fn); + } + + return exports; +} diff --git a/packages/tui/package.json b/packages/tui/package.json index 213e51bb2..c684eae75 100644 --- a/packages/tui/package.json +++ b/packages/tui/package.json @@ -12,6 +12,7 @@ }, "files": [ "dist/**/*", + "native/win32/prebuilds/**/*.node", "README.md" ], "keywords": [ @@ -38,9 +39,6 @@ "get-east-asian-width": "1.6.0", "marked": "15.0.12" }, - "optionalDependencies": { - "koffi": "2.16.2" - }, "devDependencies": { "@xterm/headless": "5.5.0", "@xterm/xterm": "5.5.0", diff --git a/packages/tui/src/terminal.ts b/packages/tui/src/terminal.ts index 9d24dcdd0..94c56ff61 100644 --- a/packages/tui/src/terminal.ts +++ b/packages/tui/src/terminal.ts @@ -1,6 +1,7 @@ import * as fs from "node:fs"; import { createRequire } from "node:module"; import * as path from "node:path"; +import { fileURLToPath } from "node:url"; import { setKittyProtocolActive } from "./keys.ts"; import { StdinBuffer } from "./stdin-buffer.ts"; @@ -210,23 +211,30 @@ export class ProcessTerminal implements Terminal { private enableWindowsVTInput(): void { if (process.platform !== "win32") return; try { - // Dynamic require to avoid bundling koffi's 74MB of cross-platform - // native binaries into every compiled binary. Koffi is only needed - // on Windows for VT input support. - const koffi = cjsRequire("koffi"); - const k32 = koffi.load("kernel32.dll"); - const GetStdHandle = k32.func("void* __stdcall GetStdHandle(int)"); - const GetConsoleMode = k32.func("bool __stdcall GetConsoleMode(void*, _Out_ uint32_t*)"); - const SetConsoleMode = k32.func("bool __stdcall SetConsoleMode(void*, uint32_t)"); + const arch = process.arch; + if (arch !== "x64" && arch !== "arm64") return; - const STD_INPUT_HANDLE = -10; - const ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200; - const handle = GetStdHandle(STD_INPUT_HANDLE); - const mode = new Uint32Array(1); - GetConsoleMode(handle, mode); - SetConsoleMode(handle, mode[0]! | ENABLE_VIRTUAL_TERMINAL_INPUT); + // Dynamic require so non-Windows and bundled/browser paths never load the + // native helper. In the npm package native/ is next to dist/; in compiled + // binary archives native/ is copied next to the executable. + const moduleDir = path.dirname(fileURLToPath(import.meta.url)); + const nativePath = path.join("native", "win32", "prebuilds", `win32-${arch}`, "win32-console-mode.node"); + const candidates = [ + path.join(moduleDir, "..", nativePath), + path.join(moduleDir, nativePath), + path.join(path.dirname(process.execPath), nativePath), + ]; + for (const modulePath of candidates) { + try { + const helper = cjsRequire(modulePath) as { enableVirtualTerminalInput?: () => boolean }; + helper.enableVirtualTerminalInput?.(); + return; + } catch { + // Try the next possible packaging location. + } + } } catch { - // koffi not available — Shift+Tab won't be distinguishable from Tab + // Native helper not available — Shift+Tab won't be distinguishable from Tab. } } diff --git a/scripts/build-binaries.sh b/scripts/build-binaries.sh index 4b94de970..c1b7b8687 100755 --- a/scripts/build-binaries.sh +++ b/scripts/build-binaries.sh @@ -105,14 +105,10 @@ fi for platform in "${PLATFORMS[@]}"; do echo "Building for $platform..." - # Externalize koffi to avoid embedding all 18 platform .node files (~74MB) - # into every binary. Koffi is only used on Windows for VT input and the - # call site has a try/catch fallback. For Windows builds, we copy the - # appropriate .node file alongside the binary below. if [[ "$platform" == windows-* ]]; then - bun build --compile --external koffi --target=bun-$platform ./dist/bun/cli.js --outfile binaries/$platform/pi.exe + bun build --compile --target=bun-$platform ./dist/bun/cli.js --outfile binaries/$platform/pi.exe else - bun build --compile --external koffi --target=bun-$platform ./dist/bun/cli.js --outfile binaries/$platform/pi + bun build --compile --target=bun-$platform ./dist/bun/cli.js --outfile binaries/$platform/pi fi done @@ -132,17 +128,15 @@ for platform in "${PLATFORMS[@]}"; do cp -r docs binaries/$platform/ cp -r examples binaries/$platform/ - # Copy koffi native module for Windows (needed for VT input support) + # Copy Windows VT input native helper next to compiled Windows binaries. if [[ "$platform" == windows-* ]]; then if [[ "$platform" == "windows-arm64" ]]; then - koffi_arch_dir="win32_arm64" + win32_arch_dir="win32-arm64" else - koffi_arch_dir="win32_x64" + win32_arch_dir="win32-x64" fi - mkdir -p binaries/$platform/node_modules/koffi/build/koffi/$koffi_arch_dir - cp ../../node_modules/koffi/index.js binaries/$platform/node_modules/koffi/ - cp ../../node_modules/koffi/package.json binaries/$platform/node_modules/koffi/ - cp ../../node_modules/koffi/build/koffi/$koffi_arch_dir/koffi.node binaries/$platform/node_modules/koffi/build/koffi/$koffi_arch_dir/ + mkdir -p binaries/$platform/native/win32/prebuilds/$win32_arch_dir + cp ../tui/native/win32/prebuilds/$win32_arch_dir/win32-console-mode.node binaries/$platform/native/win32/prebuilds/$win32_arch_dir/ fi done diff --git a/scripts/generate-coding-agent-shrinkwrap.mjs b/scripts/generate-coding-agent-shrinkwrap.mjs index a122f1835..7bcf56813 100644 --- a/scripts/generate-coding-agent-shrinkwrap.mjs +++ b/scripts/generate-coding-agent-shrinkwrap.mjs @@ -12,7 +12,6 @@ const shrinkwrapPath = join(codingAgentDir, "npm-shrinkwrap.json"); const internalPackagePrefix = "@earendil-works/pi-"; const allowedInstallScriptPackages = new Map([ ["@google/genai@1.52.0", "preinstall is a no-op in the published package"], - ["koffi@2.16.2", "optional native package ships prebuilt modules used without install scripts"], ["protobufjs@7.5.9", "postinstall only warns about protobufjs version scheme mismatches"], ]); diff --git a/scripts/local-release.mjs b/scripts/local-release.mjs index 5cdc67bc1..782318bd2 100644 --- a/scripts/local-release.mjs +++ b/scripts/local-release.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; +import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, isAbsolute, join, relative, resolve } from "node:path"; import { spawnSync } from "node:child_process"; @@ -73,6 +73,7 @@ function run(command, args, options = {}) { const result = spawnSync(command, args, { cwd: options.cwd, encoding: "utf8", + shell: process.platform === "win32", stdio: options.capture ? ["inherit", "pipe", "inherit"] : "inherit", }); @@ -123,6 +124,73 @@ function fileSpecifier(fromDirectory, file) { return `file:${relativePath.startsWith(".") ? relativePath : `./${relativePath}`}`; } +function currentBinaryPlatform() { + if (process.platform === "win32") return process.arch === "arm64" ? "windows-arm64" : "windows-x64"; + if (process.platform === "darwin") return process.arch === "arm64" ? "darwin-arm64" : "darwin-x64"; + if (process.platform === "linux") return process.arch === "arm64" ? "linux-arm64" : "linux-x64"; + throw new Error(`Unsupported binary platform: ${process.platform} ${process.arch}`); +} + +function copyBinaryAssets(targetDirectory) { + const codingAgentDirectory = join(repoRoot, "packages", "coding-agent"); + const distDirectory = join(codingAgentDirectory, "dist"); + cpSync(join(codingAgentDirectory, "package.json"), join(targetDirectory, "package.json")); + cpSync(join(codingAgentDirectory, "README.md"), join(targetDirectory, "README.md")); + cpSync(join(codingAgentDirectory, "CHANGELOG.md"), join(targetDirectory, "CHANGELOG.md")); + cpSync(join(repoRoot, "node_modules", "@silvia-odwyer", "photon-node", "photon_rs_bg.wasm"), join(targetDirectory, "photon_rs_bg.wasm")); + cpSync(join(distDirectory, "modes", "interactive", "theme"), join(targetDirectory, "theme"), { recursive: true }); + cpSync(join(distDirectory, "modes", "interactive", "assets"), join(targetDirectory, "assets"), { recursive: true }); + cpSync(join(distDirectory, "core", "export-html"), join(targetDirectory, "export-html"), { recursive: true }); + cpSync(join(codingAgentDirectory, "docs"), join(targetDirectory, "docs"), { recursive: true }); + cpSync(join(codingAgentDirectory, "examples"), join(targetDirectory, "examples"), { recursive: true }); +} + +function copyWindowsConsoleModeHelper(targetDirectory, platform) { + if (!platform.startsWith("windows-")) return; + const win32Arch = platform === "windows-arm64" ? "win32-arm64" : "win32-x64"; + const relativeNativePath = join("native", "win32", "prebuilds", win32Arch); + mkdirSync(join(targetDirectory, relativeNativePath), { recursive: true }); + cpSync( + join(repoRoot, "packages", "tui", "native", "win32", "prebuilds", win32Arch, "win32-console-mode.node"), + join(targetDirectory, relativeNativePath, "win32-console-mode.node"), + ); +} + +function buildBunBinaryRelease(targetDirectory, archiveDirectory) { + if (!commandExists("bun")) { + throw new Error("Bun is required for the local binary release build."); + } + const platform = currentBinaryPlatform(); + mkdirSync(targetDirectory, { recursive: true }); + const executableName = platform.startsWith("windows-") ? "pi.exe" : "pi"; + const executablePath = join(targetDirectory, executableName); + const target = `bun-${platform}`; + run("bun", ["build", "--compile", `--target=${target}`, join(repoRoot, "packages", "coding-agent", "dist", "bun", "cli.js"), "--outfile", executablePath]); + copyBinaryAssets(targetDirectory); + copyWindowsConsoleModeHelper(targetDirectory, platform); + if (platform.startsWith("windows-")) { + run("powershell", ["-NoProfile", "-Command", `Compress-Archive -Path '${join(targetDirectory, "*").replaceAll("'", "''")}' -DestinationPath '${join(archiveDirectory, `pi-${platform}.zip`).replaceAll("'", "''")}' -Force`]); + } else { + run("tar", ["-czf", join(archiveDirectory, `pi-${platform}.tar.gz`), "-C", targetDirectory, "."]); + } + return platform; +} + +function createPiShim(installDirectory) { + const binDirectory = join(installDirectory, "node_modules", ".bin"); + if (process.platform === "win32") { + if (existsSync(join(binDirectory, "pi.cmd"))) { + writeFileSync(join(installDirectory, "pi.cmd"), '@ECHO off\r\n"%~dp0node_modules\\.bin\\pi.cmd" %*\r\n'); + writeFileSync(join(installDirectory, "pi.ps1"), '& "$PSScriptRoot/node_modules/.bin/pi.ps1" @args\n'); + return; + } + writeFileSync(join(installDirectory, "pi.cmd"), '@ECHO off\r\n"%~dp0node_modules\\.bin\\pi.exe" %*\r\n'); + writeFileSync(join(installDirectory, "pi.ps1"), '& "$PSScriptRoot/node_modules/.bin/pi.exe" @args\n'); + return; + } + symlinkSync(join("node_modules", ".bin", "pi"), join(installDirectory, "pi")); +} + function packPackage(pkg, tarballDirectory) { const packageJson = readPackageJson(pkg.directory); if (packageJson.name !== pkg.name) { @@ -148,7 +216,8 @@ if (rootPackageJson.name !== "pi-monorepo") { const outDir = prepareOutputDirectory(options, repoRoot); const tarballDirectory = join(outDir, "tarballs"); const nodeInstallDirectory = join(outDir, "node"); -const bunInstallDirectory = join(outDir, "bun"); +const bunInstallDirectory = join(outDir, "bun-install"); +const binaryDirectory = join(outDir, "bun"); mkdirSync(tarballDirectory, { recursive: true }); if (!options.skipCheck) { @@ -166,16 +235,19 @@ for (const pkg of packages) { tarballs.set(pkg.name, tarball); } +let binaryPlatform; if (!options.skipInstall) { + binaryPlatform = buildBunBinaryRelease(binaryDirectory, outDir); + mkdirSync(nodeInstallDirectory, { recursive: true }); const dependencies = Object.fromEntries( packages.map((pkg) => [pkg.name, fileSpecifier(nodeInstallDirectory, tarballs.get(pkg.name))]), ); - const installPackageJson = `${JSON.stringify({ private: true, dependencies }, undefined, "\t")}\n`; + const installPackageJson = `${JSON.stringify({ private: true, dependencies, overrides: dependencies }, undefined, "\t")}\n`; writeFileSync(join(nodeInstallDirectory, "package.json"), installPackageJson); run("npm", ["install", "--omit=dev", "--ignore-scripts"], { cwd: nodeInstallDirectory }); - symlinkSync(join("node_modules", ".bin", "pi"), join(nodeInstallDirectory, "pi")); + createPiShim(nodeInstallDirectory); if (!options.skipBunInstall) { if (!commandExists("bun")) { @@ -185,9 +257,9 @@ if (!options.skipInstall) { const bunDependencies = Object.fromEntries( packages.map((pkg) => [pkg.name, fileSpecifier(bunInstallDirectory, tarballs.get(pkg.name))]), ); - writeFileSync(join(bunInstallDirectory, "package.json"), `${JSON.stringify({ private: true, dependencies: bunDependencies }, undefined, "\t")}\n`); + writeFileSync(join(bunInstallDirectory, "package.json"), `${JSON.stringify({ private: true, dependencies: bunDependencies, overrides: bunDependencies }, undefined, "\t")}\n`); run("bun", ["install", "--production", "--ignore-scripts"], { cwd: bunInstallDirectory }); - symlinkSync(join("node_modules", ".bin", "pi"), join(bunInstallDirectory, "pi")); + createPiShim(bunInstallDirectory); } } @@ -199,15 +271,21 @@ for (const tarball of tarballs.values()) { } if (!options.skipInstall) { + console.log("\nLocal Bun binary release:"); + console.log(` ${binaryDirectory}`); + console.log(` ${join(outDir, `pi-${binaryPlatform}.${String(binaryPlatform).startsWith("windows-") ? "zip" : "tar.gz"}`)}`); + console.log("\nRun the local Bun binary release from outside the repository:"); + console.log(` ${join(binaryDirectory, String(binaryPlatform).startsWith("windows-") ? "pi.exe" : "pi")} --help`); + console.log("\nIsolated npm install:"); console.log(` ${nodeInstallDirectory}`); console.log("\nRun the locally packed npm CLI from outside the repository:"); - console.log(` ${join(nodeInstallDirectory, "pi")} --help`); + console.log(` ${join(nodeInstallDirectory, process.platform === "win32" ? "pi.cmd" : "pi")} --help`); if (!options.skipBunInstall) { - console.log("\nIsolated Bun install:"); + console.log("\nIsolated Bun package install:"); console.log(` ${bunInstallDirectory}`); - console.log("\nRun the locally packed Bun CLI from outside the repository:"); - console.log(` ${join(bunInstallDirectory, "pi")} --help`); + console.log("\nRun the locally packed Bun package CLI from outside the repository:"); + console.log(` ${join(bunInstallDirectory, process.platform === "win32" ? "pi.cmd" : "pi")} --help`); } }