mirror of
https://github.com/anomalyco/opencode.git
synced 2026-04-24 06:45:22 +00:00
replace the shim we dont control
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from "fs"
|
||||
import crypto from "crypto"
|
||||
import path from "path"
|
||||
import os from "os"
|
||||
import { fileURLToPath } from "url"
|
||||
import { createRequire } from "module"
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
function detectPlatformAndArch() {
|
||||
@@ -49,60 +48,91 @@ function detectPlatformAndArch() {
|
||||
|
||||
function findBinary() {
|
||||
const { platform, arch } = detectPlatformAndArch()
|
||||
const packageName = `opencode-${platform}-${arch}`
|
||||
const binaryName = platform === "windows" ? "opencode.exe" : "opencode"
|
||||
|
||||
try {
|
||||
// Use require.resolve to find the package
|
||||
const packageJsonPath = require.resolve(`${packageName}/package.json`)
|
||||
const packageDir = path.dirname(packageJsonPath)
|
||||
const binaryPath = path.join(packageDir, "bin", binaryName)
|
||||
// Try modern variant first, then baseline (for CPUs without AVX2)
|
||||
const candidates = [`opencode-${platform}-${arch}`, `opencode-${platform}-${arch}-baseline`]
|
||||
|
||||
if (!fs.existsSync(binaryPath)) {
|
||||
throw new Error(`Binary not found at ${binaryPath}`)
|
||||
for (const packageName of candidates) {
|
||||
try {
|
||||
const packageJsonPath = require.resolve(`${packageName}/package.json`)
|
||||
const packageDir = path.dirname(packageJsonPath)
|
||||
const binaryPath = path.join(packageDir, "bin", binaryName)
|
||||
|
||||
if (fs.existsSync(binaryPath)) {
|
||||
return { binaryPath, binaryName }
|
||||
}
|
||||
} catch {
|
||||
// Package not installed, try next candidate
|
||||
}
|
||||
|
||||
return { binaryPath, binaryName }
|
||||
} catch (error) {
|
||||
throw new Error(`Could not find package ${packageName}: ${error.message}`)
|
||||
}
|
||||
|
||||
throw new Error(`Could not find platform binary for ${platform}-${arch}`)
|
||||
}
|
||||
|
||||
function prepareBinDirectory(binaryName) {
|
||||
const binDir = path.join(__dirname, "bin")
|
||||
const targetPath = path.join(binDir, binaryName)
|
||||
function findBunShimPath() {
|
||||
// Mirrors Bun's openGlobalBinDir() resolution order from
|
||||
// src/install/PackageManager/PackageManagerOptions.zig:201-233
|
||||
// 1. $BUN_INSTALL_BIN
|
||||
// 2. (bunfig.toml globalBinDir — not accessible from postinstall)
|
||||
// 3. $BUN_INSTALL/bin
|
||||
// 4. $XDG_CACHE_HOME/.bun/bin
|
||||
// 5. $HOME/.bun/bin
|
||||
const candidates = [
|
||||
process.env.BUN_INSTALL_BIN,
|
||||
process.env.BUN_INSTALL && path.join(process.env.BUN_INSTALL, "bin"),
|
||||
process.env.XDG_CACHE_HOME && path.join(process.env.XDG_CACHE_HOME, ".bun", "bin"),
|
||||
path.join(os.homedir(), ".bun", "bin"),
|
||||
]
|
||||
|
||||
// Ensure bin directory exists
|
||||
if (!fs.existsSync(binDir)) {
|
||||
fs.mkdirSync(binDir, { recursive: true })
|
||||
for (const binDir of candidates) {
|
||||
if (!binDir) continue
|
||||
const shimPath = path.join(binDir, "opencode.exe")
|
||||
if (fs.existsSync(shimPath)) {
|
||||
return shimPath
|
||||
}
|
||||
}
|
||||
|
||||
// Remove existing binary/symlink if it exists
|
||||
if (fs.existsSync(targetPath)) {
|
||||
fs.unlinkSync(targetPath)
|
||||
}
|
||||
|
||||
return { binDir, targetPath }
|
||||
return null
|
||||
}
|
||||
|
||||
function symlinkBinary(sourcePath, binaryName) {
|
||||
const { targetPath } = prepareBinDirectory(binaryName)
|
||||
|
||||
fs.symlinkSync(sourcePath, targetPath)
|
||||
console.log(`opencode binary symlinked: ${targetPath} -> ${sourcePath}`)
|
||||
|
||||
// Verify the file exists after operation
|
||||
if (!fs.existsSync(targetPath)) {
|
||||
throw new Error(`Failed to symlink binary to ${targetPath}`)
|
||||
function replaceBunShim(realBinaryPath) {
|
||||
const shimPath = findBunShimPath()
|
||||
if (!shimPath) {
|
||||
console.log("Bun shim not found, skipping replacement")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the shim is already the real binary by comparing file hashes
|
||||
const shimHash = crypto.createHash("sha256").update(fs.readFileSync(shimPath)).digest("hex")
|
||||
const realHash = crypto.createHash("sha256").update(fs.readFileSync(realBinaryPath)).digest("hex")
|
||||
if (shimHash === realHash) {
|
||||
console.log("Bun shim already replaced with real binary")
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Replacing Bun shim with real binary")
|
||||
console.log(` shim: ${shimPath}`)
|
||||
console.log(` real: ${realBinaryPath}`)
|
||||
fs.copyFileSync(realBinaryPath, shimPath)
|
||||
console.log("Bun shim replaced successfully")
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
if (os.platform() === "win32") {
|
||||
// On Windows, the .exe is already included in the package and bin field points to it
|
||||
// No postinstall setup needed
|
||||
console.log("Windows detected: binary setup not needed (using packaged .exe)")
|
||||
// On Windows, Bun's global install creates a tiny shim exe (~15KB) that
|
||||
// launches the JS wrapper via spawnSync. This shim receives CTRL_C_EVENT
|
||||
// from the Windows console and dies instantly, killing the real opencode
|
||||
// process before it can handle the signal gracefully.
|
||||
//
|
||||
// Fix: replace the shim with the real compiled binary so Ctrl+C goes
|
||||
// directly to opencode's signal handler (which uses the anomalyco/bun
|
||||
// fork with the SIGINT fix from PR #25876).
|
||||
const agent = (process.env.npm_config_user_agent || "").toLowerCase()
|
||||
if (agent.startsWith("bun")) {
|
||||
const { binaryPath } = findBinary()
|
||||
replaceBunShim(binaryPath)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user