Add hub auto-launch: XDG autostart, API endpoints, and frontend toggle

Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/0b0d70c0-01d1-49d1-b9ca-8d4f8e5af64a

Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-04-07 01:26:11 +00:00
committed by GitHub
parent 27502c6997
commit 13e3b76c88
4 changed files with 134 additions and 0 deletions

View File

@@ -57,6 +57,7 @@ ZEUS_CONNECT_FILE = "/var/lib/secrets/zeus-connect-url"
REBOOT_COMMAND = ["reboot"]
ONBOARDING_FLAG = "/var/lib/sovran/onboarding-complete"
AUTOLAUNCH_DISABLE_FLAG = "/var/lib/sovran/hub-autolaunch-disabled"
# ── Tech Support constants ────────────────────────────────────────
@@ -1390,6 +1391,39 @@ async def api_onboarding_complete():
return {"ok": True}
# ── Auto-launch endpoints ─────────────────────────────────────────
@app.get("/api/autolaunch/status")
async def api_autolaunch_status():
"""Check if Hub auto-launch on login is enabled."""
disabled = os.path.exists(AUTOLAUNCH_DISABLE_FLAG)
return {"enabled": not disabled}
class AutolaunchToggleRequest(BaseModel):
enabled: bool
@app.post("/api/autolaunch/toggle")
async def api_autolaunch_toggle(req: AutolaunchToggleRequest):
"""Enable or disable Hub auto-launch on login."""
if req.enabled:
# Remove the disable flag to enable auto-launch
try:
os.remove(AUTOLAUNCH_DISABLE_FLAG)
except FileNotFoundError:
pass
else:
# Create the disable flag to suppress auto-launch
os.makedirs(os.path.dirname(AUTOLAUNCH_DISABLE_FLAG), exist_ok=True)
try:
with open(AUTOLAUNCH_DISABLE_FLAG, "w") as f:
f.write("")
except OSError as exc:
raise HTTPException(status_code=500, detail=f"Could not write flag file: {exc}")
return {"ok": True, "enabled": req.enabled}
@app.get("/api/config")
async def api_config():
cfg = load_config()

View File

@@ -105,12 +105,14 @@ async function init() {
if (cfg.feature_manager) {
loadFeatureManager();
}
loadAutolaunchToggle();
} catch (_) {
await refreshServices();
loadNetwork();
checkUpdates();
setInterval(refreshServices, POLL_INTERVAL_SERVICES);
setInterval(checkUpdates, POLL_INTERVAL_UPDATES);
loadAutolaunchToggle();
}
}

View File

@@ -579,3 +579,66 @@ function buildFeatureCard(feat) {
return card;
}
// ── Auto-launch toggle ────────────────────────────────────────────
async function loadAutolaunchToggle() {
try {
var data = await apiFetch("/api/autolaunch/status");
renderAutolaunchToggle(data.enabled);
} catch (err) {
console.warn("Failed to load autolaunch status:", err);
}
}
function renderAutolaunchToggle(enabled) {
// Remove existing section if any
var old = $sidebarFeatures.querySelector(".autolaunch-section");
if (old) old.parentNode.removeChild(old);
var section = document.createElement("div");
section.className = "category-section autolaunch-section";
section.innerHTML =
'<div class="section-header">Preferences</div>' +
'<hr class="section-divider" />' +
'<div class="feature-card">' +
'<div class="feature-card-top">' +
'<div class="feature-card-info">' +
'<div class="feature-card-name">Auto-launch Hub on Login</div>' +
'<div class="feature-card-desc">Automatically open the Sovran Hub dashboard in your browser when you log in to the desktop.</div>' +
'</div>' +
'<label class="feature-toggle' + (enabled ? " active" : "") + '" id="autolaunch-toggle-label" title="Toggle auto-launch">' +
'<input type="checkbox" class="feature-toggle-input" id="autolaunch-toggle-input"' + (enabled ? " checked" : "") + ' />' +
'<span class="feature-toggle-slider"></span>' +
'</label>' +
'</div>' +
'</div>';
$sidebarFeatures.appendChild(section);
var input = document.getElementById("autolaunch-toggle-input");
var label = document.getElementById("autolaunch-toggle-label");
if (!input || !label) return;
input.addEventListener("change", async function() {
var newEnabled = input.checked;
// Revert visually until confirmed
input.checked = !newEnabled;
if (newEnabled) { label.classList.remove("active"); } else { label.classList.add("active"); }
input.disabled = true;
try {
await apiFetch("/api/autolaunch/toggle", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ enabled: newEnabled }),
});
input.checked = newEnabled;
if (newEnabled) { label.classList.add("active"); } else { label.classList.remove("active"); }
} catch (err) {
alert("Failed to update auto-launch setting. Please try again.");
} finally {
input.disabled = false;
}
});
}