let _lastMintedToken = ""; function showNotice(msg, isError) { const banner = document.getElementById("status-banner"); if (!banner) return; banner.textContent = msg; banner.style.display = "block"; banner.style.background = isError ? "var(--danger-bg)" : "var(--success-bg)"; banner.style.color = isError ? "var(--danger-text)" : "var(--success-text)"; banner.style.border = isError ? "1px solid var(--danger-border)" : "1px solid var(--success-border)"; setTimeout(() => { banner.style.display = "none"; }, 5000); } globalThis.showNotice = showNotice; function openDelegateDrawer() { const drawer = document.getElementById("delegateDrawer"); if (drawer) { drawer.style.display = "block"; drawer.offsetHeight; drawer.classList.add("open"); const panel = drawer.querySelector(".drawer-panel"); if (panel) panel.classList.add("open"); } } globalThis.openDelegateDrawer = openDelegateDrawer; function closeDelegateDrawer() { const drawer = document.getElementById("delegateDrawer"); if (drawer) { drawer.classList.remove("open"); const panel = drawer.querySelector(".drawer-panel"); if (panel) panel.classList.remove("open"); setTimeout(() => { drawer.style.display = "none"; }, 250); } } globalThis.closeDelegateDrawer = closeDelegateDrawer; function selectLifespan(btn, hours) { document.querySelectorAll(".lifespan-pill").forEach((b) => b.classList.remove("active") ); btn.classList.add("active"); const input = document.getElementById("delegateHours"); if (input) input.value = hours; } globalThis.selectLifespan = selectLifespan; function selectMode(btn, mode) { document.querySelectorAll(".mode-pill").forEach((b) => b.classList.remove("active") ); btn.classList.add("active"); const input = document.getElementById("delegateMode"); if (input) input.value = mode; const matrix = document.getElementById("customScopeMatrix"); if (matrix) { matrix.style.display = mode === "custom" ? "block" : "none"; } } globalThis.selectMode = selectMode; function handleTargetAppChange(appName) { if (appName) { const scopeVal = "app:" + appName; document.querySelectorAll(".scope-checkbox").forEach((cb) => { if (cb.value === scopeVal) cb.checked = true; }); } } globalThis.handleTargetAppChange = handleTargetAppChange; async function handleDelegateSession(e) { e.preventDefault(); const labelInput = document.getElementById("delegateLabel"); const label = labelInput ? labelInput.value.trim() : "Delegated Session"; const hoursInput = document.getElementById("delegateHours"); const lifespanHours = parseInt(hoursInput ? hoursInput.value : "1", 10) || 1; const modeInput = document.getElementById("delegateMode"); let mode = modeInput ? modeInput.value : "read"; const targetAppInput = document.getElementById("delegateTargetApp"); const targetApp = targetAppInput ? targetAppInput.value : ""; const customScopes = []; if (targetApp) { customScopes.push("app:" + targetApp); } if (mode === "custom" || targetApp) { document.querySelectorAll(".scope-checkbox:checked").forEach((cb) => { if (!customScopes.includes(cb.value)) { customScopes.push(cb.value); } }); if (targetApp && mode !== "custom" && mode !== "admin") { mode = "custom"; } } const btn = document.getElementById("submitDelegateBtn"); if (btn) { btn.disabled = true; btn.textContent = "Minting..."; } try { const res = await fetch("/api/sessions/delegate", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ label, lifespanHours, mode, customScopes }), }); const data = await res.json(); if (res.ok) { _lastMintedToken = data.token; const origin = globalThis.location ? globalThis.location.origin : ""; const magicElem = document.getElementById("handoffMagicLinkText"); if (magicElem) { magicElem.textContent = origin + "/pass?token=" + data.token; } const cliElem = document.getElementById("handoffCliText"); if (cliElem) { cliElem.textContent = 'export AUTH_YES_TOKEN="' + data.token + '"'; } const curlElem = document.getElementById("handoffCurlText"); if (curlElem) { curlElem.textContent = '-H "Authorization: Bearer ' + data.token + '"'; } const handoffModal = document.getElementById("handoffModal"); if (handoffModal) handoffModal.style.display = "block"; showNotice("Delegated session created: " + data.label, false); } else { showNotice(data.error || "Failed to delegate session", true); } } catch (_err) { showNotice("Network error delegating session", true); } finally { if (btn) { btn.disabled = false; btn.textContent = "Mint & Delegate Session"; } } } globalThis.handleDelegateSession = handleDelegateSession; function copyHandoff(type) { let text = ""; if (type === "cli") { const el = document.getElementById("handoffCliText"); if (el) text = el.textContent; } if (type === "curl") { const el = document.getElementById("handoffCurlText"); if (el) text = el.textContent; } if (type === "magic") { const el = document.getElementById("handoffMagicLinkText"); if (el) text = el.textContent; } if (navigator.clipboard) { navigator.clipboard.writeText(text); showNotice("Copied to clipboard!", false); } } globalThis.copyHandoff = copyHandoff; function closeHandoffModal() { const modal = document.getElementById("handoffModal"); if (modal) modal.style.display = "none"; if (globalThis.location) globalThis.location.reload(); } globalThis.closeHandoffModal = closeHandoffModal; function openEditScopesModal(sessionId, label, scopesJson) { let scopes = []; try { scopes = typeof scopesJson === "string" ? JSON.parse(scopesJson) : scopesJson; if (typeof scopes === "string") scopes = JSON.parse(scopes); } catch (_e) { scopes = []; } const idInput = document.getElementById("editScopesSessionId"); if (idInput) idInput.value = sessionId; const labelEl = document.getElementById("editScopesLabel"); if (labelEl) labelEl.textContent = label; document.querySelectorAll(".edit-scope-chk").forEach((cb) => { cb.checked = Array.isArray(scopes) && scopes.includes(cb.value); }); const modal = document.getElementById("editScopesModal"); if (modal) modal.style.display = "flex"; } globalThis.openEditScopesModal = openEditScopesModal; function closeEditScopesModal() { const modal = document.getElementById("editScopesModal"); if (modal) modal.style.display = "none"; } globalThis.closeEditScopesModal = closeEditScopesModal; async function saveUpdatedScopes() { const idInput = document.getElementById("editScopesSessionId"); const sessionId = idInput ? idInput.value : ""; const customScopes = []; document.querySelectorAll(".edit-scope-chk:checked").forEach((cb) => { customScopes.push(cb.value); }); try { const res = await fetch("/api/sessions/" + sessionId + "/scopes", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ customScopes }), }); if (res.ok) { showNotice("Session scopes updated!", false); setTimeout(() => { if (globalThis.location) globalThis.location.reload(); }, 500); } else { const data = await res.json(); showNotice(data.error || "Failed to update scopes", true); } } catch (_err) { showNotice("Network error updating scopes", true); } } globalThis.saveUpdatedScopes = saveUpdatedScopes; function copyText(text) { if (navigator.clipboard) { navigator.clipboard.writeText(text); showNotice("Copied to clipboard!", false); } } globalThis.copyText = copyText; // Global escape listener for modals and drawers document.addEventListener("keydown", (e) => { if (e.key === "Escape") { closeDelegateDrawer(); closeEditScopesModal(); } });