// Baixa o release `*-shared` oficial do k2-fsa/sherpa-onnx v1.12.23 e extrai // apenas o binário `sherpa-onnx-offline` para `src-tauri/binaries/sherpa-onnx-offline-`. // // O shared lib já vem com as DLLs/so de `onnxruntime` no diretório `bin/`. Essas // DLLs adicionais são empacotadas como `resources` do Tauri (ver tauri.conf.json) // para que o executável as encontre em produção. // // Uso: // node scripts/setup-sidecar.mjs // // Variáveis de ambiente opcionais: // SHERPA_ONNX_VERSION (default: v1.12.23) // SHERPA_ONNX_TRIPLE (default: detectado via `rustc -vV`) import { execFileSync } from "node:child_process"; import { existsSync, mkdirSync, chmodSync, createWriteStream, statSync, renameSync, copyFileSync, } from "node:fs"; import { dirname, join, resolve, sep as pathSep } from "node:path"; import { pipeline } from "node:stream/promises"; import { fileURLToPath } from "node:url"; import process from "node:process"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const repoRoot = resolve(__dirname, ".."); const binariesDir = join(repoRoot, "src-tauri", "binaries"); mkdirSync(binariesDir, { recursive: true }); const DEFAULT_VERSION = "v1.12.23"; const PLATFORM_MAP = [ { keys: ["linux", "x64"], asset: "sherpa-onnx-v1.12.23-linux-x64-shared.tar.bz2", triple: "x86_64-unknown-linux-gnu", ext: "" }, { keys: ["linux", "arm64"], asset: "sherpa-onnx-v1.12.23-linux-aarch64-shared.tar.bz2", triple: "aarch64-unknown-linux-gnu", ext: "" }, { keys: ["darwin", "x64"], asset: "sherpa-onnx-v1.12.23-osx-universal2-shared.tar.bz2", triple: "x86_64-apple-darwin", ext: "" }, { keys: ["darwin", "arm64"], asset: "sherpa-onnx-v1.12.23-osx-universal2-shared.tar.bz2", triple: "aarch64-apple-darwin", ext: "" }, { keys: ["win32", "x64"], asset: "sherpa-onnx-v1.12.23-win-x64-shared.tar.bz2", triple: "x86_64-pc-windows-msvc", ext: ".exe" }, ]; const RELEASE_BASE = "https://github.com/k2-fsa/sherpa-onnx/releases/download"; function detectTriple() { if (process.env.SHERPA_ONNX_TRIPLE) return process.env.SHERPA_ONNX_TRIPLE; try { const output = execFileSync("rustc", ["-vV"], { encoding: "utf8" }); const line = output.split("\n").find((l) => l.startsWith("host:")); if (line) return line.replace("host:", "").trim(); } catch { // cai para detecção por platform/arch } const os = process.platform; const archName = process.arch; return PLATFORM_MAP.find( (p) => (p.keys[0] === "win32" ? os === "win32" : p.keys[0] === os) && p.keys[1] === archName, )?.triple; } function pickAsset(triple) { const found = PLATFORM_MAP.find((p) => p.triple === triple); if (!found) { throw new Error( `Não há binário pré-buildado para o target ${triple}. Edite scripts/setup-sidecar.mjs para apontar para um build customizado.`, ); } return found; } async function download(url, dest) { const res = await fetch(url); if (!res.ok) { throw new Error(`download falhou: ${res.status} ${res.statusText} em ${url}`); } if (!res.body) { throw new Error(`resposta vazia em ${url}`); } await pipeline(res.body, createWriteStream(dest)); return statSync(dest).size; } function extractTarBzip2(archive, cwd) { // `tar` (bsdtar) no Windows não aceita bem paths com backslash nem `D:` // em `-C`. Usamos `cwd` + path relativo para evitar isso. const prefix = cwd.endsWith(pathSep) ? cwd : cwd + pathSep; const relArchive = archive.startsWith(prefix) ? archive.slice(prefix.length) : archive; execFileSync("tar", ["-xjf", relArchive], { stdio: "inherit", cwd }); } function moveExtractedBin(extractedBinDir, expectedNames, dest) { for (const name of expectedNames) { const candidate = join(extractedBinDir, name); if (existsSync(candidate)) { renameSync(candidate, dest); return; } } throw new Error( `binário sherpa-onnx-offline não encontrado em ${extractedBinDir}; nomes esperados: ${expectedNames.join(", ")}`, ); } function copyExtraSharedLibs(extractedRoot, ext) { // As DLLs adicionais (onnxruntime, providers_shared) precisam acompanhar o // executável em runtime. Copiamos para `src-tauri/binaries/` lado a lado // do binário renomeado; o empacotamento final via tauri.conf.json // resources cuida de movê-las para junto do .exe no instalador. const binDir = join(extractedRoot, "bin"); if (!existsSync(binDir)) return; for (const file of ["onnxruntime.dll", "onnxruntime_providers_shared.dll", "cargs.dll"]) { const src = join(binDir, file); if (existsSync(src)) { const destName = ext === ".exe" ? file : file.replace(/\.dll$/, ".so"); copyFileSync(src, join(binariesDir, destName)); } } } async function main() { const triple = detectTriple(); if (!triple) { throw new Error("Não foi possível detectar o target triple do host."); } const asset = pickAsset(triple); const version = process.env.SHERPA_ONNX_VERSION || DEFAULT_VERSION; const assetName = asset.asset.replace(/v1\.12\.23/, version); const versionNoV = version.replace(/^v/, ""); const url = `${RELEASE_BASE}/v${versionNoV}/${assetName}`; const finalPath = join(binariesDir, `sherpa-onnx-offline-${triple}${asset.ext}`); if (existsSync(finalPath) && statSync(finalPath).size > 1024 * 1024) { console.log(`✓ sidecar já presente em ${finalPath} (${statSync(finalPath).size} bytes)`); return; } const archivePath = join(binariesDir, assetName); console.log(`↓ baixando ${url}`); const size = await download(url, archivePath); console.log(`✓ ${size} bytes baixados em ${archivePath}`); console.log("↓ extraindo…"); extractTarBzip2(archivePath, binariesDir); const extractedRoot = join(binariesDir, assetName.replace(/\.tar\.bz2$/, "")); const expected = asset.ext === ".exe" ? ["sherpa-onnx-offline.exe", "sherpa-onnx-offline"] : ["sherpa-onnx-offline"]; moveExtractedBin(join(extractedRoot, "bin"), expected, finalPath); copyExtraSharedLibs(extractedRoot, asset.ext); if (asset.ext === "") { try { chmodSync(finalPath, 0o755); } catch { // ignore em Windows } } console.log(`✓ sidecar pronto em ${finalPath}`); } main().catch((error) => { console.error(`falha: ${error.message}`); process.exitCode = 1; });