mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-10 11:39:45 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b246824fdb | |||
| e9db6c4209 |
@@ -244,6 +244,7 @@ for (const item of targets) {
|
|||||||
{
|
{
|
||||||
name,
|
name,
|
||||||
version: Script.version,
|
version: Script.version,
|
||||||
|
preferUnplugged: true,
|
||||||
os: [item.os],
|
os: [item.os],
|
||||||
cpu: [item.arch],
|
cpu: [item.arch],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,102 +1,189 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import childProcess from "child_process"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import path from "path"
|
|
||||||
import os from "os"
|
import os from "os"
|
||||||
import { fileURLToPath } from "url"
|
import path from "path"
|
||||||
import { createRequire } from "module"
|
import { createRequire } from "module"
|
||||||
|
import { fileURLToPath } from "url"
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
const require = createRequire(import.meta.url)
|
const require = createRequire(import.meta.url)
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8"))
|
||||||
|
|
||||||
function detectPlatformAndArch() {
|
const platformMap = {
|
||||||
// Map platform names
|
darwin: "darwin",
|
||||||
let platform
|
linux: "linux",
|
||||||
switch (os.platform()) {
|
win32: "windows",
|
||||||
case "darwin":
|
}
|
||||||
platform = "darwin"
|
const archMap = {
|
||||||
break
|
x64: "x64",
|
||||||
case "linux":
|
arm64: "arm64",
|
||||||
platform = "linux"
|
arm: "arm",
|
||||||
break
|
|
||||||
case "win32":
|
|
||||||
platform = "windows"
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
platform = os.platform()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map architecture names
|
|
||||||
let arch
|
|
||||||
switch (os.arch()) {
|
|
||||||
case "x64":
|
|
||||||
arch = "x64"
|
|
||||||
break
|
|
||||||
case "arm64":
|
|
||||||
arch = "arm64"
|
|
||||||
break
|
|
||||||
case "arm":
|
|
||||||
arch = "arm"
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
arch = os.arch()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return { platform, arch }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function findBinary() {
|
const platform = platformMap[os.platform()] ?? os.platform()
|
||||||
const { platform, arch } = detectPlatformAndArch()
|
const arch = archMap[os.arch()] ?? os.arch()
|
||||||
const packageName = `opencode-${platform}-${arch}`
|
const base = `opencode-${platform}-${arch}`
|
||||||
const binaryName = platform === "windows" ? "opencode.exe" : "opencode"
|
const sourceBinary = platform === "windows" ? "opencode.exe" : "opencode"
|
||||||
|
const targetBinary = path.join(__dirname, "bin", "opencode.exe")
|
||||||
|
|
||||||
try {
|
function supportsAvx2() {
|
||||||
// Use require.resolve to find the package
|
if (arch !== "x64") return false
|
||||||
const packageJsonPath = require.resolve(`${packageName}/package.json`)
|
|
||||||
const packageDir = path.dirname(packageJsonPath)
|
|
||||||
const binaryPath = path.join(packageDir, "bin", binaryName)
|
|
||||||
|
|
||||||
if (!fs.existsSync(binaryPath)) {
|
if (platform === "linux") {
|
||||||
throw new Error(`Binary not found at ${binaryPath}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return { binaryPath, binaryName }
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(`Could not find package ${packageName}: ${error.message}`, { cause: error })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// On non-Windows platforms, just verify the binary package exists
|
|
||||||
// Don't replace the wrapper script - it handles binary execution
|
|
||||||
const { binaryPath } = findBinary()
|
|
||||||
const target = path.join(__dirname, "bin", ".opencode")
|
|
||||||
if (fs.existsSync(target)) fs.unlinkSync(target)
|
|
||||||
try {
|
try {
|
||||||
fs.linkSync(binaryPath, target)
|
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
|
||||||
} catch {
|
} catch {
|
||||||
fs.copyFileSync(binaryPath, target)
|
return false
|
||||||
}
|
}
|
||||||
fs.chmodSync(target, 0o755)
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to setup opencode binary:", error.message)
|
|
||||||
process.exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 command =
|
||||||
|
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
|
||||||
|
|
||||||
|
for (const executable of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
|
||||||
|
try {
|
||||||
|
const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command", command], {
|
||||||
|
encoding: "utf8",
|
||||||
|
timeout: 3000,
|
||||||
|
windowsHide: true,
|
||||||
|
})
|
||||||
|
if (result.status !== 0) continue
|
||||||
|
const output = (result.stdout || "").trim().toLowerCase()
|
||||||
|
if (output === "true" || output === "1") return true
|
||||||
|
if (output === "false" || output === "0") return false
|
||||||
|
} catch {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMusl() {
|
||||||
|
if (platform !== "linux") return false
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (fs.existsSync("/etc/alpine-release")) return true
|
||||||
|
} catch {
|
||||||
|
// Ignore filesystem probes that are blocked by the host.
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
|
||||||
|
return `${result.stdout || ""}${result.stderr || ""}`.toLowerCase().includes("musl")
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function packageNames() {
|
||||||
|
const baseline = arch === "x64" && !supportsAvx2()
|
||||||
|
|
||||||
|
if (platform === "linux") {
|
||||||
|
if (isMusl()) {
|
||||||
|
if (arch === "x64")
|
||||||
|
return baseline
|
||||||
|
? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
|
||||||
|
: [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
|
||||||
|
return [`${base}-musl`, base]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arch === "x64")
|
||||||
|
return baseline
|
||||||
|
? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
|
||||||
|
: [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
|
||||||
|
return [base, `${base}-musl`]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arch === "x64") return baseline ? [`${base}-baseline`, base] : [base, `${base}-baseline`]
|
||||||
|
return [base]
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveBinary(name) {
|
||||||
|
const packageJsonPath = require.resolve(`${name}/package.json`)
|
||||||
|
const binaryPath = path.join(path.dirname(packageJsonPath), "bin", sourceBinary)
|
||||||
|
if (!fs.existsSync(binaryPath)) throw new Error(`Binary not found at ${binaryPath}`)
|
||||||
|
return binaryPath
|
||||||
|
}
|
||||||
|
|
||||||
|
function installPackage(name) {
|
||||||
|
const version = packageJson.optionalDependencies?.[name]
|
||||||
|
if (!version) return
|
||||||
|
|
||||||
|
const temp = fs.mkdtempSync(path.join(os.tmpdir(), "opencode-install-"))
|
||||||
|
try {
|
||||||
|
const result = childProcess.spawnSync(
|
||||||
|
"npm",
|
||||||
|
["install", "--ignore-scripts", "--no-save", "--loglevel=error", "--prefix", temp, `${name}@${version}`],
|
||||||
|
{ stdio: "inherit", windowsHide: true },
|
||||||
|
)
|
||||||
|
if (result.status !== 0) return
|
||||||
|
const packageDir = path.join(temp, "node_modules", name)
|
||||||
|
copyBinary(path.join(packageDir, "bin", sourceBinary), targetBinary)
|
||||||
|
return true
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(temp, { recursive: true, force: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyBinary(source, target) {
|
||||||
|
if (!fs.existsSync(source)) throw new Error(`Binary not found at ${source}`)
|
||||||
|
fs.mkdirSync(path.dirname(target), { recursive: true })
|
||||||
|
if (fs.existsSync(target)) fs.unlinkSync(target)
|
||||||
|
try {
|
||||||
|
fs.linkSync(source, target)
|
||||||
|
} catch {
|
||||||
|
fs.copyFileSync(source, target)
|
||||||
|
}
|
||||||
|
fs.chmodSync(target, 0o755)
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyBinary() {
|
||||||
|
const result = childProcess.spawnSync(targetBinary, ["--version"], {
|
||||||
|
encoding: "utf8",
|
||||||
|
stdio: "ignore",
|
||||||
|
windowsHide: true,
|
||||||
|
})
|
||||||
|
return result.status === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
for (const name of packageNames()) {
|
||||||
|
try {
|
||||||
|
copyBinary(resolveBinary(name), targetBinary)
|
||||||
|
if (verifyBinary()) return
|
||||||
|
} catch {
|
||||||
|
if (installPackage(name) && verifyBinary()) return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`It seems your package manager failed to install the right opencode CLI package. Try manually installing ${packageNames()
|
||||||
|
.map((name) => JSON.stringify(name))
|
||||||
|
.join(" or ")}.`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
void main()
|
main()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Postinstall script error:", error.message)
|
console.error(error.message)
|
||||||
process.exit(0)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,22 +32,32 @@ console.log("binaries", binaries)
|
|||||||
const version = Object.values(binaries)[0]
|
const version = Object.values(binaries)[0]
|
||||||
|
|
||||||
await $`mkdir -p ./dist/${pkg.name}`
|
await $`mkdir -p ./dist/${pkg.name}`
|
||||||
await $`cp -r ./bin ./dist/${pkg.name}/bin`
|
await $`mkdir -p ./dist/${pkg.name}/bin`
|
||||||
await $`cp ./script/postinstall.mjs ./dist/${pkg.name}/postinstall.mjs`
|
await $`cp ./script/postinstall.mjs ./dist/${pkg.name}/postinstall.mjs`
|
||||||
await Bun.file(`./dist/${pkg.name}/LICENSE`).write(await Bun.file("../../LICENSE").text())
|
await Bun.file(`./dist/${pkg.name}/LICENSE`).write(await Bun.file("../../LICENSE").text())
|
||||||
|
await Bun.file(`./dist/${pkg.name}/bin/${pkg.name}.exe`).write(
|
||||||
|
[
|
||||||
|
"#!/usr/bin/env node",
|
||||||
|
"console.error('The opencode native binary was not installed. Run `node postinstall.mjs` from the opencode-ai package directory to finish setup.')",
|
||||||
|
"process.exit(1)",
|
||||||
|
"",
|
||||||
|
].join("\n"),
|
||||||
|
)
|
||||||
|
|
||||||
await Bun.file(`./dist/${pkg.name}/package.json`).write(
|
await Bun.file(`./dist/${pkg.name}/package.json`).write(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
{
|
{
|
||||||
name: pkg.name + "-ai",
|
name: pkg.name + "-ai",
|
||||||
bin: {
|
bin: {
|
||||||
[pkg.name]: `./bin/${pkg.name}`,
|
[pkg.name]: `./bin/${pkg.name}.exe`,
|
||||||
},
|
},
|
||||||
scripts: {
|
scripts: {
|
||||||
postinstall: "bun ./postinstall.mjs || node ./postinstall.mjs",
|
postinstall: "node ./postinstall.mjs",
|
||||||
},
|
},
|
||||||
version: version,
|
version: version,
|
||||||
license: pkg.license,
|
license: pkg.license,
|
||||||
|
os: ["darwin", "linux", "win32"],
|
||||||
|
cpu: ["arm64", "x64"],
|
||||||
optionalDependencies: binaries,
|
optionalDependencies: binaries,
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
|
|||||||
Reference in New Issue
Block a user