opencode2.cjs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. #!/usr/bin/env node
  2. const childProcess = require("child_process")
  3. const fs = require("fs")
  4. const path = require("path")
  5. const os = require("os")
  6. const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"]
  7. function run(target) {
  8. const child = childProcess.spawn(target, process.argv.slice(2), { stdio: "inherit" })
  9. child.on("error", (error) => {
  10. console.error(error.message)
  11. process.exit(1)
  12. })
  13. const forwarders = {}
  14. for (const signal of forwardedSignals) {
  15. forwarders[signal] = () => {
  16. try {
  17. child.kill(signal)
  18. } catch {}
  19. }
  20. process.on(signal, forwarders[signal])
  21. }
  22. child.on("exit", (code, signal) => {
  23. for (const forwardedSignal of forwardedSignals) process.removeListener(forwardedSignal, forwarders[forwardedSignal])
  24. if (signal) return process.kill(process.pid, signal)
  25. process.exit(typeof code === "number" ? code : 0)
  26. })
  27. }
  28. const envPath = process.env.OPENCODE_BIN_PATH
  29. const scriptDir = path.dirname(fs.realpathSync(__filename))
  30. const command = path.basename(__filename).replace(/\.cjs$/, "")
  31. const nodeBuild = command === "opencode2-node"
  32. const cached = path.join(scriptDir, `.${command}`)
  33. const platform = { darwin: "darwin", linux: "linux", win32: "windows" }[os.platform()] || os.platform()
  34. const arch = { x64: "x64", arm64: "arm64", arm: "arm" }[os.arch()] || os.arch()
  35. const base = `@opencode-ai/cli${nodeBuild ? "-node" : ""}-` + platform + "-" + arch
  36. const binary = platform === "windows" ? `${command}.exe` : command
  37. function supportsAvx2() {
  38. if (arch !== "x64") return false
  39. if (platform === "linux") {
  40. try {
  41. return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
  42. } catch {
  43. return false
  44. }
  45. }
  46. if (platform === "darwin") {
  47. try {
  48. const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { encoding: "utf8", timeout: 1500 })
  49. return result.status === 0 && (result.stdout || "").trim() === "1"
  50. } catch {
  51. return false
  52. }
  53. }
  54. if (platform === "windows") {
  55. const command =
  56. '(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
  57. for (const executable of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
  58. try {
  59. const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command", command], {
  60. encoding: "utf8",
  61. timeout: 3000,
  62. windowsHide: true,
  63. })
  64. if (result.status !== 0) continue
  65. const output = (result.stdout || "").trim().toLowerCase()
  66. if (output === "true" || output === "1") return true
  67. if (output === "false" || output === "0") return false
  68. } catch {
  69. continue
  70. }
  71. }
  72. }
  73. return false
  74. }
  75. const names = (() => {
  76. if (nodeBuild) return [base]
  77. const baseline = arch === "x64" && !supportsAvx2()
  78. if (platform === "linux") {
  79. const musl = (() => {
  80. try {
  81. if (fs.existsSync("/etc/alpine-release")) return true
  82. const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
  83. return ((result.stdout || "") + (result.stderr || "")).toLowerCase().includes("musl")
  84. } catch {
  85. return false
  86. }
  87. })()
  88. if (musl)
  89. return arch === "x64"
  90. ? baseline
  91. ? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
  92. : [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
  93. : [`${base}-musl`, base]
  94. return arch === "x64"
  95. ? baseline
  96. ? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
  97. : [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
  98. : [base, `${base}-musl`]
  99. }
  100. return arch === "x64" ? (baseline ? [`${base}-baseline`, base] : [base, `${base}-baseline`]) : [base]
  101. })()
  102. function findBinary(startDir) {
  103. let current = startDir
  104. for (;;) {
  105. const modules = path.join(current, "node_modules")
  106. if (fs.existsSync(modules))
  107. for (const name of names) {
  108. const candidate = path.join(modules, name, "bin", binary)
  109. if (fs.existsSync(candidate)) return candidate
  110. }
  111. const parent = path.dirname(current)
  112. if (parent === current) return
  113. current = parent
  114. }
  115. }
  116. const resolved = envPath || (fs.existsSync(cached) ? cached : findBinary(scriptDir))
  117. if (!resolved) {
  118. console.error(
  119. `It seems that your package manager failed to install the right ${command} CLI package. Try manually installing ` +
  120. names.map((name) => `"${name}"`).join(" or ") +
  121. " package",
  122. )
  123. process.exit(1)
  124. }
  125. run(resolved)