Claude connector panel: split site approvals from add-on-panel approvals
Site approvals and panel approvals share one allowedOrigins list on disk because the policy is one SitePolicy either way, but on screen they are two different things: a URL you signed in on versus an Aegis sidebar panel you unlocked. The panel showed them in one mono list under "Sites Claude may control", so an Aegis approval read as "theseus-panel://aegis" with no label and no clue it was a panel at all. publicState() now splits allowedOrigins into allowedSites (web origins) and allowedPanels (theseus-panel://<addon>, enriched with the add-on's own title via api.tabControl.list()). The panel.html renders them in their own sections: "Sites Claude may control" and "Add-on panels Claude may control", the latter hidden when nothing is approved. Each panel row shows the add-on title in bold with the mono origin as a subtitle. The ✕ button goes through the existing remove-origin IPC — removing either kind takes one call. The renderer falls back to deriving the split from allowedOrigins so a UI loaded against an older host still renders both sections.
This commit is contained in:
parent
baf86fc5a6
commit
a04775ad0d
3 changed files with 45 additions and 3 deletions
|
|
@ -50,7 +50,10 @@ code change.
|
|||
closed panels too so a client can see what exists; `webContents(-N)`
|
||||
throws "<addon> is not open; ask the user to open the panel from the
|
||||
sidebar first" until the user docks it. Password-field refusal and the
|
||||
action log still apply.
|
||||
action log still apply. The connector panel.html now splits site
|
||||
approvals from panel approvals into their own sections, with the add-on's
|
||||
own title ("Aegis panel") above the mono origin string and a ✕ Remove
|
||||
button that goes through the existing `remove-origin` IPC.
|
||||
|
||||
## session: `parallel:collision-policy`
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,26 @@ module.exports = {
|
|||
function publicState() {
|
||||
const st = server ? server.status() : { listening: false, port: null, connected: false, clients: [], sessions: [] };
|
||||
const url = server && server.url;
|
||||
// Panel approvals (theseus-panel://<addonId>) and site approvals share
|
||||
// the allowedOrigins list on disk. Split them for the UI so each gets
|
||||
// its own section, and enrich panel rows with the add-on's own title.
|
||||
const PANEL_PREFIX = "theseus-panel://";
|
||||
const panelTitles = new Map();
|
||||
try {
|
||||
for (const t of api.tabControl.list()) {
|
||||
if (t && t.kind === "panel" && t.addon) panelTitles.set(String(t.addon).toLowerCase(), t.title || t.addon);
|
||||
}
|
||||
} catch {}
|
||||
const allowedPanels = [];
|
||||
const allowedSites = [];
|
||||
for (const o of S.allowedOrigins) {
|
||||
if (typeof o === "string" && o.startsWith(PANEL_PREFIX)) {
|
||||
const addon = o.slice(PANEL_PREFIX.length);
|
||||
allowedPanels.push({ origin: o, addon, title: panelTitles.get(addon.toLowerCase()) || addon.replace(/^\w/, (c) => c.toUpperCase()) });
|
||||
} else {
|
||||
allowedSites.push(o);
|
||||
}
|
||||
}
|
||||
return {
|
||||
enabled: true,
|
||||
accept: S.accept,
|
||||
|
|
@ -128,6 +148,8 @@ module.exports = {
|
|||
controlledTabs: controller.controlledTabIds(),
|
||||
allowEvaluate: S.allowEvaluate,
|
||||
allowedOrigins: S.allowedOrigins,
|
||||
allowedSites,
|
||||
allowedPanels,
|
||||
uploadDirs: S.uploadDirs,
|
||||
sessionLimit: S.sessionLimit,
|
||||
token: S.token,
|
||||
|
|
|
|||
|
|
@ -116,6 +116,12 @@
|
|||
<ul class="list" id="origins"></ul>
|
||||
</section>
|
||||
|
||||
<section id="panelsSection" hidden>
|
||||
<h2>Add-on panels Claude may control</h2>
|
||||
<p>Each add-on is approved once. The approval covers every panel owned by that add-on, and the panel must be open in the sidebar for Claude to read or act on it.</p>
|
||||
<ul class="list" id="panels"></ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Upload folders</h2>
|
||||
<p>Claude can attach files only from these folders.</p>
|
||||
|
|
@ -156,9 +162,20 @@
|
|||
$("setup").hidden = !st.commands;
|
||||
renderApps(st.apps);
|
||||
if (st.commands) { $("cmdCode").textContent = st.commands.claudeCode; $("cmdDesk").textContent = st.commands.claudeDesktop; }
|
||||
$("origins").innerHTML = st.allowedOrigins.length
|
||||
? st.allowedOrigins.map((o) => `<li><span class="grow mono">${esc(o)}</span><button class="x" data-origin="${esc(o)}" title="Remove">✕</button></li>`).join("")
|
||||
// Site approvals (http(s)) and panel approvals (theseus-panel://) are
|
||||
// kept apart on screen even though they share one list on disk, so the
|
||||
// user sees them for what they are. publicState() does the split and
|
||||
// enriches each panel with the add-on's own title; the fallback keeps
|
||||
// older hosts rendering the mixed list the way they used to.
|
||||
const sites = Array.isArray(st.allowedSites) ? st.allowedSites : (st.allowedOrigins || []).filter((o) => !String(o).startsWith("theseus-panel://"));
|
||||
const panels = Array.isArray(st.allowedPanels) ? st.allowedPanels : (st.allowedOrigins || []).filter((o) => String(o).startsWith("theseus-panel://")).map((o) => ({ origin: o, addon: o.slice("theseus-panel://".length), title: o.slice("theseus-panel://".length).replace(/^\w/, (c) => c.toUpperCase()) }));
|
||||
$("origins").innerHTML = sites.length
|
||||
? sites.map((o) => `<li><span class="grow mono">${esc(o)}</span><button class="x" data-origin="${esc(o)}" title="Remove">✕</button></li>`).join("")
|
||||
: '<li class="empty">None yet.</li>';
|
||||
$("panelsSection").hidden = panels.length === 0;
|
||||
$("panels").innerHTML = panels.length
|
||||
? panels.map((p) => `<li><span class="grow"><b>${esc(p.title)} panel</b><div class="note mono">${esc(p.origin)}</div></span><button class="x" data-origin="${esc(p.origin)}" title="Remove">✕</button></li>`).join("")
|
||||
: "";
|
||||
$("dirs").innerHTML = st.uploadDirs.length
|
||||
? st.uploadDirs.map((d) => `<li><span class="grow mono">${esc(d)}</span><button class="x" data-dir="${esc(d)}" title="Remove">✕</button></li>`).join("")
|
||||
: '<li class="empty">None. Uploads are refused.</li>';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue