app
Adom Bridge
Public Made by Adomby adom
Adom Bridge unleashes your AI onto your full PC: full power, full safety. The AI breaks out of the container it runs in and onto your real machine, managing and running your entire laptop as you: launch apps, move files, drive any window, control KiCad, Fusion 360 or your real signed-in browser. Works with any AI, cloud or local (Claude Desktop, Claude Code, Codex), no Hydrogen required. Bridge and its bridges are an operating system for AI, with managed Node and Python runtimes, and a human-onl
← Commit history
web-control-demo.html+30−7@@ -186,17 +186,40 @@ for (const w of mine) { // WHO is "this machine": ab 2.0.53+ reports its relay client name at // /status.machine, so every card can say "on AdomLapper" instead of a vague // "this machine" (John: "always tell me which machine this demo is running on"). - var selfName = null; + var selfName = null, selfVersion = null, latestVersion = null; function loadSelfName() { return fetch(AB + "/status").then(function (r) { return r.json(); }).then(function (s) { var m = s && s.machine; if (m && (m.clientName || m.hostname)) selfName = m.clientName || m.hostname; + if (s && s.version) selfVersion = s.version; return selfName; }).catch(function () { return null; }); } - loadSelfName().then(function () { - // Name the "self" option as soon as we know who self IS - no Detect needed. - if (selfName && sel.options.length && !sel.options[0].value) sel.options[0].textContent = "this machine (" + selfName + ")"; + // The LATEST published ab, from the wiki's own update manifest (the same file + // the auto-updater reads) - so the picker can say whether a machine is + // up to date, not just what it runs (John, 2026-08-17). + function loadLatestVersion() { + return fetch("https://wiki.adom.inc/api/v1/pages/adom-bridge/files/version.json") + .then(function (r) { return r.json(); }) + .then(function (v) { latestVersion = v && v.version; return latestVersion; }) + .catch(function () { return null; }); + } + function verParts(v) { return String(v || "0").split(".").map(function (x) { return parseInt(x, 10) || 0; }); } + function verBehind(v) { // true when v < latestVersion + if (!v || !latestVersion) return false; + var a = verParts(v), b = verParts(latestVersion); + for (var i = 0; i < 3; i++) { if ((a[i] || 0) < (b[i] || 0)) return true; if ((a[i] || 0) > (b[i] || 0)) return false; } + return false; + } + function verTag(v) { // "(v2.0.54 - update available)" | "(v2.0.55, latest)" | "(v2.0.55)" + if (!v) return ""; + if (!latestVersion) return " (v" + v + ")"; + return verBehind(v) ? " (v" + v + " - update available, latest " + latestVersion + ")" : " (v" + v + ", latest)"; + } + function selfOptionLabel() { return "this machine (" + selfLabel() + ")" + (selfVersion ? verTag(selfVersion) : ""); } + Promise.all([loadSelfName(), loadLatestVersion()]).then(function () { + // Name + version the "self" option as soon as we know - no Detect needed. + if (selfName && sel.options.length && !sel.options[0].value) sel.options[0].textContent = selfOptionLabel(); }); // The label for where a run lands. Verbs that pass no target run on SELF. function machineLabel(target) { @@ -1068,10 +1091,10 @@ for (const w of mine) { var n = (t.name || t.hostname || "").toLowerCase(); return !(selfName && n === selfName.toLowerCase()); }); - sel.innerHTML = '<option value="">this machine (' + esc(selfLabel()) + ')</option>'; - peers.forEach(function (t) { var o = document.createElement("option"); o.value = t.name || t.hostname; o.textContent = (t.name || t.hostname) + (t.version ? " (v" + t.version + ")" : ""); sel.appendChild(o); }); + sel.innerHTML = '<option value="">' + esc(selfOptionLabel()) + '</option>'; + peers.forEach(function (t) { var o = document.createElement("option"); o.value = t.name || t.hostname; o.textContent = (t.name || t.hostname) + verTag(t.version); sel.appendChild(o); }); if (peers.length >= 1) { var a = document.createElement("option"); a.value = "all"; a.textContent = "ALL machines (fan out)"; sel.appendChild(a); } - document.getElementById("target-count").textContent = selfLabel() + (peers.length ? " + " + peers.length + " more" : " only"); + document.getElementById("target-count").textContent = selfLabel() + (peers.length ? " + " + peers.length + " more" : " only") + (latestVersion ? " ยท latest ab v" + latestVersion : ""); }) .catch(function (e) { btn.disabled = false; fetchDied(e);