diff --git a/packages/opencode/bin/opencode b/packages/opencode/bin/opencode index a7101f42b0fe..2c39d63d2a84 100755 --- a/packages/opencode/bin/opencode +++ b/packages/opencode/bin/opencode @@ -1,199 +1,28 @@ -#!/usr/bin/env node - -const childProcess = require("child_process") -const fs = require("fs") -const path = require("path") -const os = require("os") - -const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"] - -function run(target) { - const child = childProcess.spawn(target, process.argv.slice(2), { - stdio: "inherit", - }) - - child.on("error", (error) => { - console.error(error.message) - process.exit(1) - }) - - const forwarders = {} - for (const signal of forwardedSignals) { - forwarders[signal] = () => { - try { - child.kill(signal) - } catch { - // The child may have already exited. - } - } - process.on(signal, forwarders[signal]) - } - - child.on("exit", (code, signal) => { - for (const forwardedSignal of forwardedSignals) { - process.removeListener(forwardedSignal, forwarders[forwardedSignal]) - } - - if (signal) { - process.kill(process.pid, signal) - return - } - - process.exit(typeof code === "number" ? code : 0) - }) -} - -const envPath = process.env.OPENCODE_BIN_PATH - -const scriptPath = fs.realpathSync(__filename) -const scriptDir = path.dirname(scriptPath) - -// -const cached = path.join(scriptDir, ".opencode") - -const platformMap = { - darwin: "darwin", - linux: "linux", - win32: "windows", -} -const archMap = { - x64: "x64", - arm64: "arm64", - arm: "arm", -} - -let platform = platformMap[os.platform()] -if (!platform) { - platform = os.platform() -} -let arch = archMap[os.arch()] -if (!arch) { - arch = os.arch() -} -const base = "opencode-" + platform + "-" + arch -const binary = platform === "windows" ? "opencode.exe" : "opencode" - -function supportsAvx2() { - if (arch !== "x64") return false - - if (platform === "linux") { - try { - return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8")) - } catch { - return false - } - } - - if (platform === "darwin") { - try { - const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { - encoding: "utf8", - timeout: 1500, - }) - if (result.status !== 0) return false - return (result.stdout || "").trim() === "1" - } catch { - return false - } - } - - if (platform === "windows") { - const cmd = - '(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)' - - for (const exe of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) { - try { - const result = childProcess.spawnSync(exe, ["-NoProfile", "-NonInteractive", "-Command", cmd], { - encoding: "utf8", - timeout: 3000, - windowsHide: true, - }) - if (result.status !== 0) continue - const out = (result.stdout || "").trim().toLowerCase() - if (out === "true" || out === "1") return true - if (out === "false" || out === "0") return false - } catch { - continue - } - } - - return false - } - - return false -} - -const names = (() => { - const avx2 = supportsAvx2() - const baseline = arch === "x64" && !avx2 - - if (platform === "linux") { - const musl = (() => { - try { - if (fs.existsSync("/etc/alpine-release")) return true - } catch { - // ignore - } - - try { - const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" }) - const text = ((result.stdout || "") + (result.stderr || "")).toLowerCase() - if (text.includes("musl")) return true - } catch { - // ignore - } - - return false - })() - - if (musl) { - if (arch === "x64") { - if (baseline) return [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base] - return [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`] - } - return [`${base}-musl`, base] - } - - if (arch === "x64") { - if (baseline) return [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`] - return [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`] - } - return [base, `${base}-musl`] - } - - if (arch === "x64") { - if (baseline) return [`${base}-baseline`, base] - return [base, `${base}-baseline`] - } - return [base] -})() - -function findBinary(startDir) { - let current = startDir - for (;;) { - const modules = path.join(current, "node_modules") - if (fs.existsSync(modules)) { - for (const name of names) { - const candidate = path.join(modules, name, "bin", binary) - if (fs.existsSync(candidate)) return candidate - } - } - const parent = path.dirname(current) - if (parent === current) { - return - } - current = parent - } -} - -const resolved = envPath || (fs.existsSync(cached) ? cached : findBinary(scriptDir)) -if (!resolved) { - console.error( - "It seems that your package manager failed to install the right version of the opencode CLI for your platform. You can try manually installing " + - names.map((n) => `\"${n}\"`).join(" or ") + - " package", - ) - process.exit(1) -} - -run(resolved) +#!/bin/sh +set -e + +# Resolve the directory containing this script, following symlinks. +prog="$0" +while [ -L "$prog" ]; do + dir="$(cd "$(dirname "$prog")" && pwd)" + prog="$(readlink "$prog")" + case "$prog" in + /*) ;; + *) prog="$dir/$prog" ;; + esac +done +dir="$(cd "$(dirname "$prog")" && pwd)" + +# Check for env var override. +if [ -n "$OPENCODE_BIN_PATH" ]; then + exec "$OPENCODE_BIN_PATH" "$@" +fi + +# Resolve the binary path via the Node.js resolver. +binary=$("$dir/opencode-resolve" 2>/dev/null) || true +if [ -n "$binary" ] && [ -x "$binary" ]; then + exec "$binary" "$@" +fi + +# Fallback: let the resolver show its error message. +exec node "$dir/opencode-resolve" diff --git a/packages/opencode/bin/opencode-resolve b/packages/opencode/bin/opencode-resolve new file mode 100755 index 000000000000..077e743b9b7e --- /dev/null +++ b/packages/opencode/bin/opencode-resolve @@ -0,0 +1,160 @@ +#!/usr/bin/env node + +const childProcess = require("child_process") +const fs = require("fs") +const path = require("path") +const os = require("os") + +const envPath = process.env.OPENCODE_BIN_PATH + +const scriptPath = fs.realpathSync(__filename) +const scriptDir = path.dirname(scriptPath) + +const cached = path.join(scriptDir, ".opencode") + +const platformMap = { + darwin: "darwin", + linux: "linux", + win32: "windows", +} +const archMap = { + x64: "x64", + arm64: "arm64", + arm: "arm", +} + +let platform = platformMap[os.platform()] +if (!platform) { + platform = os.platform() +} +let arch = archMap[os.arch()] +if (!arch) { + arch = os.arch() +} +const base = "opencode-" + platform + "-" + arch +const binary = platform === "windows" ? "opencode.exe" : "opencode" + +function supportsAvx2() { + if (arch !== "x64") return false + + if (platform === "linux") { + try { + return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8")) + } catch { + return false + } + } + + if (platform === "darwin") { + try { + const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { + encoding: "utf8", + timeout: 1500, + }) + if (result.status !== 0) return false + return (result.stdout || "").trim() === "1" + } catch { + return false + } + } + + if (platform === "windows") { + const cmd = + '(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)' + + for (const exe of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) { + try { + const result = childProcess.spawnSync(exe, ["-NoProfile", "-NonInteractive", "-Command", cmd], { + encoding: "utf8", + timeout: 3000, + windowsHide: true, + }) + if (result.status !== 0) continue + const out = (result.stdout || "").trim().toLowerCase() + if (out === "true" || out === "1") return true + if (out === "false" || out === "0") return false + } catch { + continue + } + } + + return false + } + + return false +} + +const names = (() => { + const avx2 = supportsAvx2() + const baseline = arch === "x64" && !avx2 + + if (platform === "linux") { + const musl = (() => { + try { + if (fs.existsSync("/etc/alpine-release")) return true + } catch { + // ignore + } + + try { + const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" }) + const text = ((result.stdout || "") + (result.stderr || "")).toLowerCase() + if (text.includes("musl")) return true + } catch { + // ignore + } + + return false + })() + + if (musl) { + if (arch === "x64") { + if (baseline) return [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base] + return [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`] + } + return [`${base}-musl`, base] + } + + if (arch === "x64") { + if (baseline) return [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`] + return [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`] + } + return [base, `${base}-musl`] + } + + if (arch === "x64") { + if (baseline) return [`${base}-baseline`, base] + return [base, `${base}-baseline`] + } + return [base] +})() + +function findBinary(startDir) { + let current = startDir + for (;;) { + const modules = path.join(current, "node_modules") + if (fs.existsSync(modules)) { + for (const name of names) { + const candidate = path.join(modules, name, "bin", binary) + if (fs.existsSync(candidate)) return candidate + } + } + const parent = path.dirname(current) + if (parent === current) { + return + } + current = parent + } +} + +const resolved = envPath || (fs.existsSync(cached) ? cached : findBinary(scriptDir)) +if (!resolved) { + console.error( + "It seems that your package manager failed to install the right version of the opencode CLI for your platform. You can try manually installing " + + names.map((n) => `"${n}"`).join(" or ") + + " package", + ) + process.exit(1) +} + +process.stdout.write(resolved) diff --git a/packages/opencode/bin/opencode.cmd b/packages/opencode/bin/opencode.cmd new file mode 100644 index 000000000000..b27c0537be7d --- /dev/null +++ b/packages/opencode/bin/opencode.cmd @@ -0,0 +1,19 @@ +@echo off +setlocal + +REM Check for env var override. +if defined OPENCODE_BIN_PATH ( + "%OPENCODE_BIN_PATH%" %* + exit /b %ERRORLEVEL% +) + +REM Resolve the binary path via the Node.js resolver. +for /f "delims=" %%i in ('node "%~dp0\opencode-resolve"') do set BINARY=%%i + +if not defined BINARY ( + exit /b 1 +) + +REM Run the binary. +"%BINARY%" %* +exit /b %ERRORLEVEL%