fix: download native crypto binary at plugin startup

Add bootstrap-native.ts that downloads @matrix-org/matrix-sdk-crypto-nodejs
native binary before loading matrix-bot-sdk. This is needed because
opencode's npm doesn't run postinstall scripts, and the binary is required
for the Matrix SDK to work.
This commit is contained in:
Бородин Роман 2026-09-30 22:01:17 +03:00
parent 56aa069c64
commit 04a12d6f3e
4 changed files with 239 additions and 0 deletions

123
bootstrap-native.ts Normal file
View File

@ -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.`)
})
}

107
dist/bootstrap-native.js vendored Normal file
View File

@ -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.`);
});
}

4
dist/index.js vendored
View File

@ -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";

View File

@ -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"