fix(theseus): Ariadne's Thread card — find the installed resolver, answer in ~2 s
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.
This commit is contained in:
parent
27a1243e4d
commit
ed323713d7
1 changed files with 46 additions and 34 deletions
50
main.js
50
main.js
|
|
@ -4067,10 +4067,16 @@ ipcMain.handle("collision-reset", () => { collisions = { byName: {}, byTld: {} }
|
||||||
const ARIADNE_TASKS = ["BNS Resolver Daemon", "BNS Sia Bridge"];
|
const ARIADNE_TASKS = ["BNS Resolver Daemon", "BNS Sia Bridge"];
|
||||||
// Inno Setup's AppId + "_is1" is the uninstall registry key. Check both
|
// Inno Setup's AppId + "_is1" is the uninstall registry key. Check both
|
||||||
// native and WOW6432 in case Inno installed either way.
|
// native and WOW6432 in case Inno installed either way.
|
||||||
const ARIADNE_UNINSTALL_KEYS = [
|
// PowerShell fragment that leaves Ariadne's Inno uninstall entry in $r (or
|
||||||
'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{7E7A5F1C-3B4E-4C8A-9E1D-ARIADNERSLVR}_is1',
|
// $null). Found by DisplayName rather than by key path: the installer's
|
||||||
'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{7E7A5F1C-3B4E-4C8A-9E1D-ARIADNERSLVR}_is1',
|
// 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
|
// 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:
|
// 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"
|
// 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() {
|
function ariadneQueryState() {
|
||||||
return new Promise((resolve) => {
|
|
||||||
const { spawn } = require("child_process");
|
const { spawn } = require("child_process");
|
||||||
// One shell round-trip for both bits of info:
|
// One shell round-trip for both bits of info:
|
||||||
// - task states for BNS Resolver Daemon + BNS Sia Bridge
|
// - task states for BNS Resolver Daemon + BNS Sia Bridge
|
||||||
// - installed version + quiet-uninstall string from Inno's registry key
|
// - 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",
|
const ps = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command",
|
||||||
"$names=@('BNS Resolver Daemon','BNS Sia Bridge');" +
|
"$svc=New-Object -ComObject Schedule.Service;$svc.Connect();$f=$svc.GetFolder('\\');" +
|
||||||
"$names | ForEach-Object { $t = Get-ScheduledTask -TaskName $_ -ErrorAction SilentlyContinue;" +
|
"foreach($n in 'BNS Resolver Daemon','BNS Sia Bridge'){try{$t=$f.GetTask($n);\"$n=$($t.State)\"}catch{\"$n=MISSING\"}};" +
|
||||||
" if ($t) { \"$_=$($t.State)\" } else { \"$_=MISSING\" } };" +
|
ARIADNE_REG_LOOKUP +
|
||||||
"$keys=@(" + ARIADNE_UNINSTALL_KEYS.map((k) => "'" + k + "'").join(",") + ");" +
|
"if ($r) { \"__VER__=$($r.DisplayVersion)\"; \"__UNINSTALL__=$($r.QuietUninstallString)\" }"
|
||||||
"foreach ($k in $keys) { if (Test-Path $k) { $r=Get-ItemProperty $k; \"__VER__=$($r.DisplayVersion)\"; \"__UNINSTALL__=$($r.QuietUninstallString)\"; break } }"
|
|
||||||
], { windowsHide: true });
|
], { windowsHide: true });
|
||||||
let out = "";
|
let out = "";
|
||||||
ps.stdout.on("data", (d) => { out += d; });
|
ps.stdout.on("data", (d) => { out += d; });
|
||||||
ps.on("close", async () => {
|
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 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 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 primary = map["BNS Resolver Daemon"]; // TASK_STATE: 1 disabled, 2 queued, 3 ready, 4 running
|
||||||
const installedVersion = map.__VER__ || null;
|
const installedVersion = map.__VER__ || null;
|
||||||
const quietUninstall = map.__UNINSTALL__ || null;
|
const quietUninstall = map.__UNINSTALL__ || null;
|
||||||
const latest = await ariadneManifestFetch();
|
|
||||||
const latestVersion = latest ? latest.version : null;
|
const latestVersion = latest ? latest.version : null;
|
||||||
const canUpdate = !!(installedVersion && latestVersion && cmpVersions(latestVersion, installedVersion) > 0);
|
const canUpdate = !!(installedVersion && latestVersion && cmpVersions(latestVersion, installedVersion) > 0);
|
||||||
let state;
|
let state;
|
||||||
if (!primary || primary === "MISSING") state = installedVersion ? "stopped" : "not-installed";
|
if (!primary || primary === "MISSING") state = installedVersion ? "stopped" : "not-installed";
|
||||||
else if (primary === "Running") state = "running";
|
else if (primary === "4" || primary === "Running") state = "running";
|
||||||
else state = "stopped";
|
else state = "stopped";
|
||||||
// The "bundledVersion" field name is kept for renderer compatibility --
|
// The "bundledVersion" field name is kept for renderer compatibility --
|
||||||
// it now carries the latest version advertised by silentmode.st's
|
// it now carries the latest version advertised by silentmode.st's
|
||||||
// releases manifest, not a version physically bundled with Theseus.
|
// releases manifest, not a version physically bundled with Theseus.
|
||||||
resolve({ state, installedVersion, bundledVersion: latestVersion, canUpdate, hasUninstaller: !!quietUninstall });
|
return { state, installedVersion, bundledVersion: latestVersion, canUpdate, hasUninstaller: !!quietUninstall };
|
||||||
});
|
|
||||||
ps.on("error", () => resolve({ state: "not-installed", installedVersion: null, bundledVersion: null, canUpdate: false, hasUninstaller: false }));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function cmpVersions(a, b) {
|
function cmpVersions(a, b) {
|
||||||
|
|
@ -4263,7 +4275,7 @@ function ariadneUninstall() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Read the uninstall string fresh — a stale cache could point at a moved
|
// Read the uninstall string fresh — a stale cache could point at a moved
|
||||||
// file. Extract the path (may be quoted) + any trailing args.
|
// 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 });
|
const ps = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", cmd], { windowsHide: true });
|
||||||
let out = "";
|
let out = "";
|
||||||
ps.stdout.on("data", (d) => { out += d; });
|
ps.stdout.on("data", (d) => { out += d; });
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue