fix(adwright panel): add 1-click Switch-to-CMO button + authoritative profile fetch
Some checks failed
plugin-tests / test (push) Failing after 5s
Some checks failed
plugin-tests / test (push) Failing after 5s
JP feedback: panel says "switch to cmo profile" but offers no way to do so.
Also window.S.activeProfile may be empty on /session/* pages before boot.js
finishes initializing, so the banner showed unnecessarily.
This patch:
- Replaces vague text with a [Switch to CMO] button that POSTs to
/api/profile/switch {name: "cmo-planb"} and reloads on success
- Adds _fetchActiveProfile() that reads /api/profile/active directly
(defense against window.S race); cached in NS.state._fetchedProfile
- Background poll every 5s catches profile switches made from the upstream
Profiles panel — no hard reload needed to clear the banner
- Disabled-state refresh fires once on mount + on each poll
Karpathy 4 rules: smallest possible change (one button + one fetch helper),
no abstraction layer, no fallback "smart" detection — authoritative API only.
This commit is contained in:
parent
c6d94462c4
commit
7dcda7669f
@ -44,7 +44,11 @@
|
|||||||
// ── Active-profile detection (PRD §3 visibility gating) ────────────────
|
// ── Active-profile detection (PRD §3 visibility gating) ────────────────
|
||||||
// hermes-webui exposes S.activeProfile (see static/ui.js line 1). We probe
|
// hermes-webui exposes S.activeProfile (see static/ui.js line 1). We probe
|
||||||
// it defensively — the panel always renders, but enables only for CMO.
|
// it defensively — the panel always renders, but enables only for CMO.
|
||||||
|
// Cache from a real /api/profile/active fetch since window.S may not be
|
||||||
|
// populated yet on /session/* pages before boot.js runs.
|
||||||
|
NS.state._fetchedProfile = null;
|
||||||
function _activeProfile() {
|
function _activeProfile() {
|
||||||
|
if (NS.state._fetchedProfile) return NS.state._fetchedProfile;
|
||||||
try {
|
try {
|
||||||
return (window.S && window.S.activeProfile) || "default";
|
return (window.S && window.S.activeProfile) || "default";
|
||||||
} catch (_) { return "default"; }
|
} catch (_) { return "default"; }
|
||||||
@ -90,6 +94,24 @@
|
|||||||
_wireConnections(panel);
|
_wireConnections(panel);
|
||||||
_refreshDisabledState();
|
_refreshDisabledState();
|
||||||
_renderTab(NS.state.activeTab);
|
_renderTab(NS.state.activeTab);
|
||||||
|
// Authoritative profile fetch (window.S may be empty on /session/* paths
|
||||||
|
// before boot.js finishes). Update banner state once it arrives.
|
||||||
|
_fetchActiveProfile().then((name) => {
|
||||||
|
if (name) {
|
||||||
|
NS.state._fetchedProfile = name;
|
||||||
|
_refreshDisabledState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Re-poll every 5s so a profile switch from the upstream Profiles panel
|
||||||
|
// also lifts the banner without requiring a hard reload.
|
||||||
|
setInterval(() => {
|
||||||
|
_fetchActiveProfile().then((name) => {
|
||||||
|
if (name && name !== NS.state._fetchedProfile) {
|
||||||
|
NS.state._fetchedProfile = name;
|
||||||
|
_refreshDisabledState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
NS.state.mounted = true;
|
NS.state.mounted = true;
|
||||||
return true;
|
return true;
|
||||||
@ -116,7 +138,8 @@
|
|||||||
'<nav class="svrnty-aw-nav">' + nav + '</nav>' +
|
'<nav class="svrnty-aw-nav">' + nav + '</nav>' +
|
||||||
'<div class="svrnty-aw-content">' +
|
'<div class="svrnty-aw-content">' +
|
||||||
'<div class="svrnty-aw-disabled-banner" id="svrntyAwDisabledBanner" style="display:none">' +
|
'<div class="svrnty-aw-disabled-banner" id="svrntyAwDisabledBanner" style="display:none">' +
|
||||||
'Adwright is read-only — switch to the <strong>cmo</strong> profile to run actions.' +
|
'Adwright needs the <strong>cmo-planb</strong> profile active. ' +
|
||||||
|
'<button class="svrnty-aw-btn svrnty-aw-btn-primary" id="svrntyAwSwitchCmo">Switch to CMO</button>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
tabs +
|
tabs +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
@ -136,6 +159,38 @@
|
|||||||
if (bteBtn) bteBtn.addEventListener("click", () => {
|
if (bteBtn) bteBtn.addEventListener("click", () => {
|
||||||
if (window.SvrntyBTE && window.SvrntyBTE.open) window.SvrntyBTE.open();
|
if (window.SvrntyBTE && window.SvrntyBTE.open) window.SvrntyBTE.open();
|
||||||
});
|
});
|
||||||
|
// One-click profile switch to cmo-planb.
|
||||||
|
const switchBtn = panel.querySelector("#svrntyAwSwitchCmo");
|
||||||
|
if (switchBtn) switchBtn.addEventListener("click", async () => {
|
||||||
|
switchBtn.disabled = true;
|
||||||
|
switchBtn.textContent = "Switching…";
|
||||||
|
try {
|
||||||
|
const res = await fetch("/api/profile/switch", {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "same-origin",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ name: "cmo-planb" }),
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error("HTTP " + res.status);
|
||||||
|
// Reload to pick up new profile state across the whole webui.
|
||||||
|
location.reload();
|
||||||
|
} catch (e) {
|
||||||
|
switchBtn.disabled = false;
|
||||||
|
switchBtn.textContent = "Switch failed — retry";
|
||||||
|
console.error("[svrnty-aw] profile switch failed", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull live active profile from server (don't rely on window.S which may
|
||||||
|
// not be populated yet on /session/* pages before boot.js runs).
|
||||||
|
async function _fetchActiveProfile() {
|
||||||
|
try {
|
||||||
|
const r = await fetch("/api/profile/active", { credentials: "same-origin" });
|
||||||
|
if (!r.ok) return null;
|
||||||
|
const d = await r.json();
|
||||||
|
return (d && (d.name || d.profile)) || null;
|
||||||
|
} catch (_) { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
function _activateTab(id) {
|
function _activateTab(id) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user