From a51c46522735bd96957c54f67668ad64088e1970 Mon Sep 17 00:00:00 2001 From: KingKDot Date: Wed, 24 Jun 2026 23:40:38 -0500 Subject: [PATCH] fix --- .../src/server/plugin-state-bundle.test.ts | 26 ++++++++++ .../src/server/plugin-state-bundle.ts | 48 +++++++++++++++++-- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/Overlord-Server/src/server/plugin-state-bundle.test.ts b/Overlord-Server/src/server/plugin-state-bundle.test.ts index e844048..3c91513 100644 --- a/Overlord-Server/src/server/plugin-state-bundle.test.ts +++ b/Overlord-Server/src/server/plugin-state-bundle.test.ts @@ -6,6 +6,7 @@ import { arePluginNeedsApproved, compilePluginTypeScript, computePluginNeedsHash, + ensurePluginExtracted, loadPluginBundle, type PluginState, } from "./plugin-state-bundle"; @@ -70,6 +71,31 @@ describe("loadPluginBundle", () => { expect(bundle.size).toBe(4); }); + test("keeps helper DLLs out of the native platform binary map", async () => { + const root = await createTempRoot(); + const AdmZip = require("adm-zip"); + const zip = new AdmZip(); + zip.addFile("config.json", Buffer.from(JSON.stringify({ + name: "Plink", + apiVersion: 2, + runtime: "native", + uiEntry: "src/ui.ts", + }))); + zip.addFile("plink.html", Buffer.from("")); + zip.addFile("plink.css", Buffer.from("#plink-root { display: block; }")); + zip.addFile("src/ui.ts", Buffer.from("document.body.dataset.plugin = 'plink';")); + zip.addFile("plink-windows-amd64.dll", Buffer.from("entry-dll")); + zip.addFile("plink-payload.dll", Buffer.from("helper-dll")); + zip.writeZip(join(root, "plink.zip")); + + await ensurePluginExtracted(root, "plink", (name) => name); + const manifest = JSON.parse(await readFile(join(root, "plink", "manifest.json"), "utf-8")); + expect(manifest.binaries["windows-amd64"]).toBe("plink-windows-amd64.dll"); + + const bundle = await loadPluginBundle(root, "plink", async () => {}, "Windows 11", "amd64"); + expect(bundle.binaryPath).toBe(join(root, "plink", "plink-windows-amd64.dll")); + }); + test("computes stable need hashes and invalidates changed approvals", () => { const state: PluginState = { enabled: {}, diff --git a/Overlord-Server/src/server/plugin-state-bundle.ts b/Overlord-Server/src/server/plugin-state-bundle.ts index e3cf025..facf4ea 100644 --- a/Overlord-Server/src/server/plugin-state-bundle.ts +++ b/Overlord-Server/src/server/plugin-state-bundle.ts @@ -149,11 +149,17 @@ export async function ensurePluginExtracted( throw new Error(`Invalid plugin bundle: ${safeId} (missing .html, .css, and either .js or src/ui.ts)`); } - const binariesMap: Record = {}; for (const [filename, data] of nativeBinaries) { await fs.writeFile(path.join(pluginDir, filename), data); + } + + const binariesMap: Record = {}; + applyConfiguredNativeBinaries(binariesMap, extraConfig, nativeBinaries); + const nativeBinaryNames = Array.from(nativeBinaries.keys()) + .sort((a, b) => nativeBinaryPriority(a, safeId) - nativeBinaryPriority(b, safeId) || a.localeCompare(b)); + for (const filename of nativeBinaryNames) { const platformKey = derivePlatformKey(filename); - if (platformKey) { + if (platformKey && !binariesMap[platformKey]) { binariesMap[platformKey] = filename; } } @@ -203,6 +209,7 @@ export async function ensurePluginExtracted( runtime, version: extraConfig.version || "1.0.0", description: extraConfig.description, + ...(typeof extraConfig.binary === "string" && extraConfig.binary && { binary: extraConfig.binary }), ...(runtime === "wasm" && { wasm: String(extraConfig.wasm || wasmEntry?.filename || binariesMap["wasm32-wasi"] || "") }), ...(needs && { needs }), binaries: binariesMap, @@ -345,7 +352,7 @@ export async function loadPluginBundle( const files = await fs.readdir(dir); const archRegex = /-(linux|darwin|windows|freebsd)-(amd64|arm64|arm|386)\.(so|dll|dylib)$/i; - const found = files.find((f) => { + const found = files.find((f: string) => { const m = f.match(archRegex); if (m) { return ( @@ -733,3 +740,38 @@ function derivePlatformKey(filename: string): string { if (lower.endsWith(".so")) return "linux-amd64"; return ""; } + +function applyConfiguredNativeBinaries( + binariesMap: Record, + config: any, + nativeBinaries: Map, +): void { + if (config?.binaries && typeof config.binaries === "object") { + for (const [platformKey, filename] of Object.entries(config.binaries)) { + if (typeof platformKey !== "string" || typeof filename !== "string") continue; + const base = path.basename(filename); + if (!nativeBinaries.has(base)) continue; + binariesMap[platformKey.toLowerCase()] = base; + } + } + + if (typeof config?.binary === "string" && config.binary) { + const base = path.basename(config.binary); + if (!nativeBinaries.has(base)) return; + const platformKey = derivePlatformKey(base); + if (platformKey) { + binariesMap[platformKey] = base; + } + } +} + +function nativeBinaryPriority(filename: string, pluginId: string): number { + const escapedId = pluginId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + if (new RegExp(`^${escapedId}-(linux|darwin|windows|freebsd)-(amd64|arm64|arm|386)\\.(so|dll|dylib)$`, "i").test(filename)) { + return 0; + } + if (/-(linux|darwin|windows|freebsd)-(amd64|arm64|arm|386)\.(so|dll|dylib)$/i.test(filename)) { + return 1; + } + return 2; +}