mirror of
https://github.com/earendil-works/pi.git
synced 2026-06-18 15:54:04 +08:00
Generated
-14
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-14
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,53 @@
|
||||
#include <windows.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"],
|
||||
]);
|
||||
|
||||
|
||||
+88
-10
@@ -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`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user