diff --git a/bootstrap-native.ts b/bootstrap-native.ts new file mode 100644 index 0000000..b7ae2f9 --- /dev/null +++ b/bootstrap-native.ts @@ -0,0 +1,123 @@ +/** + * Bootstrap: downloads native binary for @matrix-org/matrix-sdk-crypto-nodejs + * if it doesn't exist. + * + * This is needed because opencode's npm doesn't run postinstall scripts. + * The binary is cached in the plugin's node_modules, so it only downloads once. + * + * Must be imported BEFORE any import from matrix-bot-sdk. + */ + +import https from "https" +import fs from "fs" +import path from "path" + +const CRYPTO_PACKAGE_DIR = path.join( + __dirname, + "..", + "node_modules", + "@matrix-org", + "matrix-sdk-crypto-nodejs", +) + +const VERSION = "0.4.0" + +function isMusl(): boolean { + if (!process.report || typeof process.report.getReport !== "function") { + try { + const lddPath = require("child_process") + .execSync("which ldd") + .toString() + .trim() + return fs.readFileSync(lddPath, "utf8").includes("musl") + } catch { + return true + } + } + const { glibcVersionRuntime } = (process.report as any).getReport().header + return !glibcVersionRuntime +} + +function getBinaryName(): string { + const platform = process.platform + const arch = process.arch + + switch (platform) { + case "linux": + if (arch === "x64") { + return isMusl() + ? "matrix-sdk-crypto.linux-x64-musl.node" + : "matrix-sdk-crypto.linux-x64-gnu.node" + } + if (arch === "arm64") { + return isMusl() + ? "matrix-sdk-crypto.linux-arm64-musl.node" + : "matrix-sdk-crypto.linux-arm64-gnu.node" + } + if (arch === "arm") { + return "matrix-sdk-crypto.linux-arm-gnueabihf.node" + } + if (arch === "s390x") { + return "matrix-sdk-crypto.linux-s390x-gnu.node" + } + break + case "darwin": + if (arch === "x64") return "matrix-sdk-crypto.darwin-x64.node" + if (arch === "arm64") return "matrix-sdk-crypto.darwin-arm64.node" + break + case "win32": + if (arch === "x64") return "matrix-sdk-crypto.win32-x64-msvc.node" + if (arch === "arm64") return "matrix-sdk-crypto.win32-arm64-msvc.node" + break + } + + throw new Error( + `Unsupported platform: ${platform}-${arch}. ` + + "matrix-bot-sdk requires a native crypto binary for this platform.", + ) +} + +export function ensureNativeBinary(): void { + const binaryName = getBinaryName() + const binaryPath = path.join(CRYPTO_PACKAGE_DIR, binaryName) + + if (fs.existsSync(binaryPath)) { + return // Already downloaded + } + + const url = `https://github.com/matrix-org/matrix-rust-sdk-crypto-nodejs/releases/download/v${VERSION}/${binaryName}` + const filePath = binaryPath + + console.error(`[matrix-plugin] Downloading native crypto binary: ${binaryName}`) + + https + .get(url, (res) => { + const statusCode = res.statusCode ?? 200 + if (statusCode >= 300 && statusCode < 400 && res.headers.location) { + // Follow redirects + https.get(res.headers.location, (redirectRes) => { + const file = fs.createWriteStream(filePath) + redirectRes.pipe(file) + file.on("finish", () => { + file.close() + console.error(`[matrix-plugin] Native binary downloaded: ${binaryName}`) + }) + }).on("error", (err) => { + console.error(`[matrix-plugin] Failed to download native binary: ${err.message}`) + console.error(`[matrix-plugin] Please ensure you have network access to GitHub releases.`) + }) + return + } + + const file = fs.createWriteStream(filePath) + res.pipe(file) + file.on("finish", () => { + file.close() + console.error(`[matrix-plugin] Native binary downloaded: ${binaryName}`) + }) + }) + .on("error", (err) => { + console.error(`[matrix-plugin] Failed to download native binary: ${err.message}`) + console.error(`[matrix-plugin] Please ensure you have network access to GitHub releases.`) + }) +} diff --git a/dist/bootstrap-native.js b/dist/bootstrap-native.js new file mode 100644 index 0000000..0e9afc9 --- /dev/null +++ b/dist/bootstrap-native.js @@ -0,0 +1,107 @@ +/** + * Bootstrap: downloads native binary for @matrix-org/matrix-sdk-crypto-nodejs + * if it doesn't exist. + * + * This is needed because opencode's npm doesn't run postinstall scripts. + * The binary is cached in the plugin's node_modules, so it only downloads once. + * + * Must be imported BEFORE any import from matrix-bot-sdk. + */ +import https from "https"; +import fs from "fs"; +import path from "path"; +const CRYPTO_PACKAGE_DIR = path.join(__dirname, "..", "node_modules", "@matrix-org", "matrix-sdk-crypto-nodejs"); +const VERSION = "0.4.0"; +function isMusl() { + if (!process.report || typeof process.report.getReport !== "function") { + try { + const lddPath = require("child_process") + .execSync("which ldd") + .toString() + .trim(); + return fs.readFileSync(lddPath, "utf8").includes("musl"); + } + catch { + return true; + } + } + const { glibcVersionRuntime } = process.report.getReport().header; + return !glibcVersionRuntime; +} +function getBinaryName() { + const platform = process.platform; + const arch = process.arch; + switch (platform) { + case "linux": + if (arch === "x64") { + return isMusl() + ? "matrix-sdk-crypto.linux-x64-musl.node" + : "matrix-sdk-crypto.linux-x64-gnu.node"; + } + if (arch === "arm64") { + return isMusl() + ? "matrix-sdk-crypto.linux-arm64-musl.node" + : "matrix-sdk-crypto.linux-arm64-gnu.node"; + } + if (arch === "arm") { + return "matrix-sdk-crypto.linux-arm-gnueabihf.node"; + } + if (arch === "s390x") { + return "matrix-sdk-crypto.linux-s390x-gnu.node"; + } + break; + case "darwin": + if (arch === "x64") + return "matrix-sdk-crypto.darwin-x64.node"; + if (arch === "arm64") + return "matrix-sdk-crypto.darwin-arm64.node"; + break; + case "win32": + if (arch === "x64") + return "matrix-sdk-crypto.win32-x64-msvc.node"; + if (arch === "arm64") + return "matrix-sdk-crypto.win32-arm64-msvc.node"; + break; + } + throw new Error(`Unsupported platform: ${platform}-${arch}. ` + + "matrix-bot-sdk requires a native crypto binary for this platform."); +} +export function ensureNativeBinary() { + const binaryName = getBinaryName(); + const binaryPath = path.join(CRYPTO_PACKAGE_DIR, binaryName); + if (fs.existsSync(binaryPath)) { + return; // Already downloaded + } + const url = `https://github.com/matrix-org/matrix-rust-sdk-crypto-nodejs/releases/download/v${VERSION}/${binaryName}`; + const filePath = binaryPath; + console.error(`[matrix-plugin] Downloading native crypto binary: ${binaryName}`); + https + .get(url, (res) => { + const statusCode = res.statusCode ?? 200; + if (statusCode >= 300 && statusCode < 400 && res.headers.location) { + // Follow redirects + https.get(res.headers.location, (redirectRes) => { + const file = fs.createWriteStream(filePath); + redirectRes.pipe(file); + file.on("finish", () => { + file.close(); + console.error(`[matrix-plugin] Native binary downloaded: ${binaryName}`); + }); + }).on("error", (err) => { + console.error(`[matrix-plugin] Failed to download native binary: ${err.message}`); + console.error(`[matrix-plugin] Please ensure you have network access to GitHub releases.`); + }); + return; + } + const file = fs.createWriteStream(filePath); + res.pipe(file); + file.on("finish", () => { + file.close(); + console.error(`[matrix-plugin] Native binary downloaded: ${binaryName}`); + }); + }) + .on("error", (err) => { + console.error(`[matrix-plugin] Failed to download native binary: ${err.message}`); + console.error(`[matrix-plugin] Please ensure you have network access to GitHub releases.`); + }); +} diff --git a/dist/index.js b/dist/index.js index 6ceb994..e625bbe 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,3 +1,7 @@ +import { ensureNativeBinary } from "./bootstrap-native.js"; +// Ensure native binary is available before loading matrix-bot-sdk +// (opencode's npm doesn't run postinstall scripts) +ensureNativeBinary(); import fs from "fs"; import os from "os"; import path from "path"; diff --git a/index.ts b/index.ts index 2a041fe..ad9093b 100644 --- a/index.ts +++ b/index.ts @@ -1,3 +1,8 @@ +import { ensureNativeBinary } from "./bootstrap-native.js" +// Ensure native binary is available before loading matrix-bot-sdk +// (opencode's npm doesn't run postinstall scripts) +ensureNativeBinary() + import fs from "fs" import os from "os" import path from "path"