opencode2.cjs 4.7 KB

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