From ed323713d70a567044598ed4f24344a4a72d2567 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Wed, 16 Sep 2026 20:15:19 +0200 Subject: [PATCH] =?UTF-8?q?fix(theseus):=20Ariadne's=20Thread=20card=20?= =?UTF-8?q?=E2=80=94=20find=20the=20installed=20resolver,=20answer=20in=20?= =?UTF-8?q?~2=20s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two reasons the Plug-ins card looked dead ("only a Refresh button"): 1. The state check ran Get-ScheduledTask, whose module import took 8–10 s cold, and only then fetched the release manifest. Every button is hidden during "checking…", so for 10–15 s the card showed nothing but Refresh. Task state now comes from the Task Scheduler COM object (numeric, locale- independent — schtasks.exe prints localized words on non-English Windows) and the manifest fetch runs in parallel: ~2 s. 2. The Inno installer's AppId is written as {{…}}, which Inno registers as {…}}_is1 (doubled closing brace). Theseus looked for the single-brace key, never found it, and so never knew the installed version — no Update button, no Uninstall button. The entry is now found by DisplayName. --- main.js | 80 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/main.js b/main.js index a43f6bc..bd32c46 100644 --- a/main.js +++ b/main.js @@ -4067,10 +4067,16 @@ ipcMain.handle("collision-reset", () => { collisions = { byName: {}, byTld: {} } const ARIADNE_TASKS = ["BNS Resolver Daemon", "BNS Sia Bridge"]; // Inno Setup's AppId + "_is1" is the uninstall registry key. Check both // native and WOW6432 in case Inno installed either way. -const ARIADNE_UNINSTALL_KEYS = [ - 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{7E7A5F1C-3B4E-4C8A-9E1D-ARIADNERSLVR}_is1', - 'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{7E7A5F1C-3B4E-4C8A-9E1D-ARIADNERSLVR}_is1', -]; +// PowerShell fragment that leaves Ariadne's Inno uninstall entry in $r (or +// $null). Found by DisplayName rather than by key path: the installer's +// AppId is written as `{{…}}`, which Inno stores as `{…}}_is1` (doubled +// closing brace), so the literal key path Theseus used to look for never +// matched — the panel showed no version, no Update, no Uninstall. Matching +// on the name also survives a future AppId fix. +const ARIADNE_REG_LOOKUP = + "$r=$null;foreach($root in 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall','HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall','HKCU:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall'){" + + "if($r){break};foreach($k in (Get-ChildItem $root -ErrorAction SilentlyContinue)){$p=Get-ItemProperty $k.PSPath -ErrorAction SilentlyContinue;" + + "if($p.DisplayName -like 'Ariadne Resolver*' -and $p.QuietUninstallString){$r=$p;break}}};"; // Same manifest the Theseus updater reads (UPDATE_MANIFEST_URL). The copy on // silentmode.st is a mirror that has lagged behind dl.silentmode.st (2026-09-09: // it still listed Theseus 0.3.31 while dl had 0.3.44), so the Ariadne "Update" @@ -4153,39 +4159,45 @@ function ariadneDownloadInstaller(entry) { } function ariadneQueryState() { - return new Promise((resolve) => { - const { spawn } = require("child_process"); - // One shell round-trip for both bits of info: - // - task states for BNS Resolver Daemon + BNS Sia Bridge - // - installed version + quiet-uninstall string from Inno's registry key + const { spawn } = require("child_process"); + // One shell round-trip for both bits of info: + // - task states for BNS Resolver Daemon + BNS Sia Bridge + // - installed version + quiet-uninstall string from Inno's registry entry + // Task state comes from the Task Scheduler COM object, not Get-ScheduledTask: + // that cmdlet imports the ScheduledTasks module, which took 8–10 s cold on the + // 0.3.48 install and left the panel on "checking…" with every button hidden + // for that long (user report 2026-09-16). The COM state is a number, so it + // is also locale-independent (schtasks.exe prints localized status words). + // The manifest fetch used to run after this shell finished; it now runs in + // parallel. + const shell = new Promise((resolve) => { const ps = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", - "$names=@('BNS Resolver Daemon','BNS Sia Bridge');" + - "$names | ForEach-Object { $t = Get-ScheduledTask -TaskName $_ -ErrorAction SilentlyContinue;" + - " if ($t) { \"$_=$($t.State)\" } else { \"$_=MISSING\" } };" + - "$keys=@(" + ARIADNE_UNINSTALL_KEYS.map((k) => "'" + k + "'").join(",") + ");" + - "foreach ($k in $keys) { if (Test-Path $k) { $r=Get-ItemProperty $k; \"__VER__=$($r.DisplayVersion)\"; \"__UNINSTALL__=$($r.QuietUninstallString)\"; break } }" + "$svc=New-Object -ComObject Schedule.Service;$svc.Connect();$f=$svc.GetFolder('\\');" + + "foreach($n in 'BNS Resolver Daemon','BNS Sia Bridge'){try{$t=$f.GetTask($n);\"$n=$($t.State)\"}catch{\"$n=MISSING\"}};" + + ARIADNE_REG_LOOKUP + + "if ($r) { \"__VER__=$($r.DisplayVersion)\"; \"__UNINSTALL__=$($r.QuietUninstallString)\" }" ], { windowsHide: true }); let out = ""; ps.stdout.on("data", (d) => { out += d; }); - ps.on("close", async () => { - const lines = out.trim().split(/\r?\n/).filter(Boolean); - const map = Object.fromEntries(lines.map((l) => { const i = l.lastIndexOf("="); return [l.slice(0, i), l.slice(i + 1)]; })); - const primary = map["BNS Resolver Daemon"]; - const installedVersion = map.__VER__ || null; - const quietUninstall = map.__UNINSTALL__ || null; - const latest = await ariadneManifestFetch(); - const latestVersion = latest ? latest.version : null; - const canUpdate = !!(installedVersion && latestVersion && cmpVersions(latestVersion, installedVersion) > 0); - let state; - if (!primary || primary === "MISSING") state = installedVersion ? "stopped" : "not-installed"; - else if (primary === "Running") state = "running"; - else state = "stopped"; - // The "bundledVersion" field name is kept for renderer compatibility -- - // it now carries the latest version advertised by silentmode.st's - // releases manifest, not a version physically bundled with Theseus. - resolve({ state, installedVersion, bundledVersion: latestVersion, canUpdate, hasUninstaller: !!quietUninstall }); - }); - ps.on("error", () => resolve({ state: "not-installed", installedVersion: null, bundledVersion: null, canUpdate: false, hasUninstaller: false })); + ps.on("close", () => resolve(out)); + ps.on("error", () => resolve("")); + }); + return Promise.all([shell, ariadneManifestFetch()]).then(([out, latest]) => { + const lines = out.trim().split(/\r?\n/).filter(Boolean); + const map = Object.fromEntries(lines.map((l) => { const i = l.lastIndexOf("="); return [l.slice(0, i), l.slice(i + 1)]; })); + const primary = map["BNS Resolver Daemon"]; // TASK_STATE: 1 disabled, 2 queued, 3 ready, 4 running + const installedVersion = map.__VER__ || null; + const quietUninstall = map.__UNINSTALL__ || null; + const latestVersion = latest ? latest.version : null; + const canUpdate = !!(installedVersion && latestVersion && cmpVersions(latestVersion, installedVersion) > 0); + let state; + if (!primary || primary === "MISSING") state = installedVersion ? "stopped" : "not-installed"; + else if (primary === "4" || primary === "Running") state = "running"; + else state = "stopped"; + // The "bundledVersion" field name is kept for renderer compatibility -- + // it now carries the latest version advertised by silentmode.st's + // releases manifest, not a version physically bundled with Theseus. + return { state, installedVersion, bundledVersion: latestVersion, canUpdate, hasUninstaller: !!quietUninstall }; }); } function cmpVersions(a, b) { @@ -4263,7 +4275,7 @@ function ariadneUninstall() { return new Promise((resolve, reject) => { // Read the uninstall string fresh — a stale cache could point at a moved // file. Extract the path (may be quoted) + any trailing args. - const cmd = `$keys=@(${ARIADNE_UNINSTALL_KEYS.map((k) => "'" + k + "'").join(",")});foreach ($k in $keys){if(Test-Path $k){$r=Get-ItemProperty $k;Write-Host $r.QuietUninstallString;break}}`; + const cmd = ARIADNE_REG_LOOKUP + "if($r){Write-Host $r.QuietUninstallString}"; const ps = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", cmd], { windowsHide: true }); let out = ""; ps.stdout.on("data", (d) => { out += d; });