- IAgentTool.ReadsForeignContent: Werkzeuge mit Aussenkontakt erklaeren sich selbst (WebFetch, WebMonitor, DirectAPI, Mail, RocketChat, Telegram, TelegramClient, SocialMediaManager, FTP, Database); AgentConfig.TreatAsForeignReader kann nur verschaerfen - ReportingLine: Stufe aus den Werkzeugen, "wer steht darueber", Pruefung des Wegs nach oben (nie Aenderungen an Aufgaben darueber, nur eine Ebene hoch, Fremdinhalt-Stufe gar kein Freitext) und Rahmung als untrusted_content - Engine: Nachricht und Spawn nach oben geprueft, Text der Fremdinhalt-Stufe kommt gerahmt an - Taskboard: task_create/update/comment, reopen und die Dubletten-Notiz folgen dem Berichtsweg - Dispatcher: Aufgaben von Agenten der Fremdinhalt-Stufe gehen gerahmt in den Lauf - TaskReports: Erledigt-Signal und Systemmeldung (in Pruefung, blockiert, abgebrochen) an den Auftraggeber, je Zustand einmal, gesammelt in einer offenen Aufgabe je Empfaenger - OrgBriefing nennt den Berichtsweg; WebUI zeigt Stufe und Schalter am Agenten Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
607 lines
20 KiB
JavaScript
607 lines
20 KiB
JavaScript
// ClawdDotNet — Agenten-Verwaltung
|
|
//
|
|
// Übersicht, Anlegen, Löschen von Agenten, Werkzeug-Schalter und maskierte Einstellungen.
|
|
|
|
import { api } from "../modules/api.js";
|
|
import { abonnieren } from "../modules/events.js";
|
|
import { t } from "../modules/i18n.js";
|
|
import {
|
|
erstelleElement,
|
|
erstelleIcon,
|
|
PFADE,
|
|
erstellePlakette,
|
|
erstelleKarte,
|
|
erstelleModal,
|
|
zeigeMeldung
|
|
} from "../modules/dom.js";
|
|
|
|
let gewaehlterAgentId = null;
|
|
let agenten = [];
|
|
let entladeEreignisse = [];
|
|
|
|
export async function rendereAgenten(parameter = {}) {
|
|
const container = document.getElementById("ansicht-inhalt");
|
|
if (!container) return;
|
|
container.replaceChildren();
|
|
|
|
entladeEreignisse.forEach((c) => c());
|
|
entladeEreignisse = [];
|
|
|
|
const layout = erstelleElement("div", { klasse: "agenten-ansicht" });
|
|
|
|
const kopfZeile = erstelleElement("div", { klasse: "ansicht-kopf" });
|
|
kopfZeile.append(
|
|
erstelleElement("h1", { text: t("agents.title") }),
|
|
erstelleElement(
|
|
"button",
|
|
{
|
|
klasse: "schaltflaeche primaer",
|
|
onclick: oeffneNeuerAgentDialog
|
|
},
|
|
erstelleIcon(PFADE.plus, 16),
|
|
erstelleElement("span", { text: ` ${t("agents.newAgent")}` })
|
|
)
|
|
);
|
|
|
|
const raster = erstelleElement("div", { klasse: "agenten-raster" });
|
|
const listeContainer = erstelleElement("div", { klasse: "agenten-liste-bereich" });
|
|
const detailContainer = erstelleElement("div", { klasse: "agenten-detail-bereich" });
|
|
|
|
raster.append(listeContainer, detailContainer);
|
|
layout.append(kopfZeile, raster);
|
|
container.append(layout);
|
|
|
|
await ladeUndRendereListe(parameter.agentId);
|
|
|
|
entladeEreignisse.push(
|
|
abonnieren("instance", (daten) => {
|
|
if (daten.kind === "Agents") {
|
|
ladeUndRendereListe(gewaehlterAgentId);
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
async function ladeUndRendereListe(zielId = null) {
|
|
const listeContainer = document.querySelector(".agenten-liste-bereich");
|
|
if (!listeContainer) return;
|
|
|
|
listeContainer.replaceChildren(
|
|
erstelleElement("div", { klasse: "hinweis-zentriert", text: t("agents.loading") })
|
|
);
|
|
|
|
try {
|
|
agenten = await api("/agents");
|
|
listeContainer.replaceChildren();
|
|
|
|
if (!agenten || agenten.length === 0) {
|
|
const leerKasten = erstelleElement("div", { klasse: "hinweis-zentriert" });
|
|
leerKasten.append(
|
|
erstelleElement("p", { klasse: "gedaempft", text: t("agents.empty") }),
|
|
erstelleElement("button", {
|
|
klasse: "schaltflaeche primaer klein",
|
|
onclick: oeffneNeuerAgentDialog,
|
|
style: "margin-top: 10px;"
|
|
}, erstelleIcon(PFADE.plus, 14), erstelleElement("span", { text: ` ${t("agents.createFirst")}` }))
|
|
);
|
|
listeContainer.append(leerKasten);
|
|
rendereDetails(null);
|
|
return;
|
|
}
|
|
|
|
const auswahlId = zielId && agenten.some((a) => a.agentId === zielId)
|
|
? zielId
|
|
: agenten[0].agentId;
|
|
|
|
for (const a of agenten) {
|
|
const eintrag = erstelleElement("div", {
|
|
klasse: `agenten-karte ${a.agentId === auswahlId ? "aktiv" : ""}`,
|
|
onclick: () => {
|
|
document.querySelectorAll(".agenten-karte").forEach((k) => k.classList.remove("aktiv"));
|
|
eintrag.classList.add("aktiv");
|
|
rendereDetails(a.agentId);
|
|
}
|
|
});
|
|
|
|
const top = erstelleElement("div", { klasse: "agent-karte-top" });
|
|
const name = erstelleElement("strong", { text: a.displayName });
|
|
const status = a.isRunning
|
|
? erstellePlakette(t("status.busy"), "warnung")
|
|
: erstellePlakette(t("status.ready"), "neutral");
|
|
top.append(name, status);
|
|
|
|
const meta = erstelleElement("div", { klasse: "agent-karte-meta" });
|
|
meta.append(
|
|
erstelleElement("span", { klasse: "gedaempft", text: `${t("agents.id")}: ${a.agentId}` }),
|
|
erstelleElement("span", { klasse: "gedaempft", text: ` · ${t("agents.model")}: ${a.model}` })
|
|
);
|
|
|
|
const werkzeugeZaehler = a.tools ? a.tools.filter((t) => t.enabled).length : 0;
|
|
const werkzeugeText = erstelleElement("div", {
|
|
klasse: "agent-karte-werkzeuge gedaempft",
|
|
text: t("agents.toolsActive", { count: werkzeugeZaehler })
|
|
});
|
|
|
|
eintrag.append(top, meta, werkzeugeText);
|
|
listeContainer.append(eintrag);
|
|
}
|
|
|
|
rendereDetails(auswahlId);
|
|
} catch (err) {
|
|
const text = err.instanzUnerreichbar
|
|
? (t("instances.instanceOfflineError") || err.message)
|
|
: (err.instanzKonflikt
|
|
? (t("instances.noInstanceActiveError") || err.message)
|
|
: err.message);
|
|
listeContainer.replaceChildren(
|
|
erstelleElement("div", { klasse: "fehler-zentriert", text })
|
|
);
|
|
}
|
|
}
|
|
|
|
async function rendereDetails(agentId) {
|
|
gewaehlterAgentId = agentId;
|
|
const detailContainer = document.querySelector(".agenten-detail-bereich");
|
|
if (!detailContainer) return;
|
|
detailContainer.replaceChildren();
|
|
|
|
if (!agentId) {
|
|
detailContainer.append(
|
|
erstelleKarte(t("agents.details"), [
|
|
erstelleElement("p", { klasse: "gedaempft", text: t("agents.selectPrompt") })
|
|
])
|
|
);
|
|
return;
|
|
}
|
|
|
|
const agent = agenten.find((a) => a.agentId === agentId);
|
|
if (!agent) return;
|
|
|
|
// Kopf-Aktionen (Chatten, Löschen)
|
|
const aktionen = erstelleElement("div", { klasse: "gruppe-horizontal" });
|
|
|
|
const zumChatBtn = erstelleElement(
|
|
"button",
|
|
{
|
|
klasse: "schaltflaeche flach",
|
|
title: t("agents.chatTooltip"),
|
|
onclick: () => {
|
|
window.location.hash = `#/chat/${encodeURIComponent(agent.agentId)}`;
|
|
}
|
|
},
|
|
erstelleIcon(PFADE.chat, 14),
|
|
erstelleElement("span", { text: ` ${t("agents.chat")}` })
|
|
);
|
|
|
|
const loeschenBtn = erstelleElement(
|
|
"button",
|
|
{
|
|
klasse: "schaltflaeche gefahr flach",
|
|
title: t("agents.deleteTooltip"),
|
|
onclick: () => loescheAgent(agent.agentId, agent.displayName)
|
|
},
|
|
erstelleIcon(PFADE.muelleimer, 14),
|
|
erstelleElement("span", { text: ` ${t("agents.delete")}` })
|
|
);
|
|
|
|
aktionen.append(zumChatBtn, loeschenBtn);
|
|
|
|
// Stammdaten-Karte
|
|
const stammdatenListe = erstelleElement("dl", { klasse: "datenliste" });
|
|
const felder = [
|
|
[t("agents.name"), agent.displayName],
|
|
[t("agents.id"), agent.agentId],
|
|
[t("agents.model"), agent.model],
|
|
[t("agents.description"), agent.description || "—"]
|
|
];
|
|
|
|
for (const [bez, wert] of felder) {
|
|
stammdatenListe.append(
|
|
erstelleElement("dt", { text: bez }),
|
|
erstelleElement("dd", { text: wert })
|
|
);
|
|
}
|
|
|
|
const basisKarte = erstelleKarte(agent.displayName, [stammdatenListe], { aktionen });
|
|
|
|
// Werkzeuge-Karte
|
|
const werkzeugeListe = erstelleElement("div", { klasse: "werkzeuge-liste" });
|
|
|
|
if (!agent.tools || agent.tools.length === 0) {
|
|
werkzeugeListe.append(
|
|
erstelleElement("p", { klasse: "gedaempft", text: t("agents.noTools") })
|
|
);
|
|
} else {
|
|
for (const tool of agent.tools) {
|
|
const zeile = erstelleElement("div", { klasse: "werkzeug-zeile" });
|
|
|
|
const links = erstelleElement("div", { klasse: "werkzeug-info" });
|
|
const toolTitel = erstelleElement("strong", { text: tool.name });
|
|
// #715: A tool that brings in outside text changes the agent's reporting line.
|
|
if (tool.readsForeignContent) {
|
|
toolTitel.append(
|
|
" ",
|
|
erstelleElement("span", { klasse: "werkzeug-fremd", text: t("agents.readsForeign") })
|
|
);
|
|
}
|
|
const toolDesc = erstelleElement("div", {
|
|
klasse: "werkzeug-beschreibung gedaempft",
|
|
text: tool.description || ""
|
|
});
|
|
links.append(toolTitel, toolDesc);
|
|
|
|
const rechts = erstelleElement("div", { klasse: "werkzeug-aktionen" });
|
|
|
|
const einstellungenBtn = erstelleElement(
|
|
"button",
|
|
{
|
|
klasse: "schaltflaeche flach klein",
|
|
title: t("agents.configTooltip"),
|
|
onclick: () => zeigeWerkzeugEinstellungen(agent.agentId, tool.name)
|
|
},
|
|
erstelleIcon(PFADE.einstellungen, 14),
|
|
erstelleElement("span", { text: ` ${t("agents.configuration")}` })
|
|
);
|
|
|
|
const toggleLabel = erstelleElement("label", { klasse: "schalter" });
|
|
const toggleInput = erstelleElement("input", {
|
|
type: "checkbox",
|
|
checked: Boolean(tool.enabled),
|
|
onchange: async (e) => {
|
|
const aktiv = e.target.checked;
|
|
try {
|
|
await api(
|
|
`/agents/${encodeURIComponent(agent.agentId)}/tools/${encodeURIComponent(tool.name)}?enabled=${aktiv}`,
|
|
{ method: "PUT" }
|
|
);
|
|
tool.enabled = aktiv;
|
|
aktualisiereBerichtsweg();
|
|
zeigeMeldung(
|
|
t("agents.toolToggled", {
|
|
tool: tool.name,
|
|
status: aktiv ? t("agents.enabled") : t("agents.disabled")
|
|
}),
|
|
"info"
|
|
);
|
|
} catch (err) {
|
|
e.target.checked = !aktiv;
|
|
zeigeMeldung(t("agents.saveFailed", { error: err.message }), "gefahr");
|
|
}
|
|
}
|
|
});
|
|
const toggleSpan = erstelleElement("span", { klasse: "schalter-regler" });
|
|
toggleLabel.append(toggleInput, toggleSpan);
|
|
|
|
rechts.append(einstellungenBtn, toggleLabel);
|
|
zeile.append(links, rechts);
|
|
werkzeugeListe.append(zeile);
|
|
}
|
|
}
|
|
|
|
const werkzeugeKarte = erstelleKarte(t("agents.availableTools"), [werkzeugeListe]);
|
|
|
|
// #715: The reporting line — the tier follows from the tools; the setting can only tighten it.
|
|
const berichtswegText = erstelleElement("p", { klasse: "gedaempft" });
|
|
const aktualisiereBerichtsweg = () => {
|
|
const quellen = (agent.tools || [])
|
|
.filter((w) => w.enabled && w.readsForeignContent)
|
|
.map((w) => w.name);
|
|
if (agent.treatAsForeignReader) quellen.push(t("agents.settingSource"));
|
|
berichtswegText.textContent = quellen.length > 0
|
|
? t("agents.tierForeign", { sources: quellen.join(", ") })
|
|
: t("agents.tierStandard");
|
|
};
|
|
aktualisiereBerichtsweg();
|
|
|
|
const verschaerfenLabel = erstelleElement("label", { klasse: "schalter" });
|
|
const verschaerfenInput = erstelleElement("input", {
|
|
type: "checkbox",
|
|
checked: Boolean(agent.treatAsForeignReader),
|
|
onchange: async (e) => {
|
|
const wert = e.target.checked;
|
|
try {
|
|
await api(
|
|
`/agents/${encodeURIComponent(agent.agentId)}/foreign-reader?value=${wert}`,
|
|
{ method: "PUT" }
|
|
);
|
|
agent.treatAsForeignReader = wert;
|
|
aktualisiereBerichtsweg();
|
|
zeigeMeldung(t("agents.foreignReaderSaved", { name: agent.displayName }), "info");
|
|
} catch (err) {
|
|
e.target.checked = !wert;
|
|
zeigeMeldung(t("agents.saveFailed", { error: err.message }), "gefahr");
|
|
}
|
|
}
|
|
});
|
|
verschaerfenLabel.append(verschaerfenInput, erstelleElement("span", { klasse: "schalter-regler" }));
|
|
|
|
const verschaerfenZeile = erstelleElement(
|
|
"div",
|
|
{ klasse: "werkzeug-zeile" },
|
|
erstelleElement(
|
|
"div",
|
|
{ klasse: "werkzeug-info" },
|
|
erstelleElement("strong", { text: t("agents.treatAsForeignReader") }),
|
|
erstelleElement("div", { klasse: "werkzeug-beschreibung gedaempft", text: t("agents.treatAsForeignReaderHint") })
|
|
),
|
|
erstelleElement("div", { klasse: "werkzeug-aktionen" }, verschaerfenLabel)
|
|
);
|
|
|
|
const berichtswegKarte = erstelleKarte(t("agents.reportingLine"), [berichtswegText, verschaerfenZeile]);
|
|
|
|
detailContainer.append(basisKarte, werkzeugeKarte, berichtswegKarte);
|
|
}
|
|
|
|
function oeffneNeuerAgentDialog() {
|
|
const formular = erstelleElement("form", {
|
|
klasse: "formular-raster",
|
|
id: "neuer-agent-formular"
|
|
});
|
|
|
|
const idFeld = erstelleElement("input", {
|
|
type: "text",
|
|
required: true,
|
|
placeholder: t("agents.idPlaceholder"),
|
|
id: "neu-agent-id"
|
|
});
|
|
|
|
const nameFeld = erstelleElement("input", {
|
|
type: "text",
|
|
required: true,
|
|
placeholder: t("agents.namePlaceholder"),
|
|
id: "neu-agent-name"
|
|
});
|
|
|
|
const modellFeld = erstelleElement("input", {
|
|
type: "text",
|
|
required: true,
|
|
placeholder: "z.B. anthropic/claude-3.5-sonnet",
|
|
value: "anthropic/claude-3.5-sonnet",
|
|
id: "neu-agent-modell"
|
|
});
|
|
|
|
const descFeld = erstelleElement("textarea", {
|
|
rows: "3",
|
|
placeholder: t("agents.descPlaceholder"),
|
|
id: "neu-agent-desc"
|
|
});
|
|
|
|
formular.append(
|
|
erstelleElement("label", { text: t("agents.idLabel") }),
|
|
idFeld,
|
|
erstelleElement("label", { text: t("agents.nameLabel") }),
|
|
nameFeld,
|
|
erstelleElement("label", { text: t("agents.modelLabel") }),
|
|
modellFeld,
|
|
erstelleElement("label", { text: t("agents.descLabel") }),
|
|
descFeld
|
|
);
|
|
|
|
const abbrechenBtn = erstelleElement(
|
|
"button",
|
|
{
|
|
type: "button",
|
|
klasse: "schaltflaeche flach",
|
|
onclick: () => modal.schliessen()
|
|
},
|
|
erstelleElement("span", { text: t("common.cancel") })
|
|
);
|
|
|
|
const speichernBtn = erstelleElement(
|
|
"button",
|
|
{
|
|
type: "submit",
|
|
klasse: "schaltflaeche primaer",
|
|
form: "neuer-agent-formular",
|
|
onclick: () => {
|
|
formular.requestSubmit();
|
|
}
|
|
},
|
|
erstelleElement("span", { text: t("agents.createButton") })
|
|
);
|
|
|
|
const modal = erstelleModal(t("agents.newAgentTitle"), formular, [abbrechenBtn, speichernBtn]);
|
|
|
|
formular.addEventListener("submit", async (e) => {
|
|
e.preventDefault();
|
|
speichernBtn.disabled = true;
|
|
|
|
try {
|
|
await api("/agents", {
|
|
method: "POST",
|
|
body: {
|
|
agentId: idFeld.value.trim(),
|
|
displayName: nameFeld.value.trim(),
|
|
model: modellFeld.value.trim(),
|
|
description: descFeld.value.trim()
|
|
}
|
|
});
|
|
modal.schliessen();
|
|
zeigeMeldung(t("agents.createdSuccess", { name: nameFeld.value.trim() }), "erfolg");
|
|
await ladeUndRendereListe(idFeld.value.trim());
|
|
} catch (err) {
|
|
zeigeMeldung(t("agents.createFailed", { error: err.message }), "gefahr");
|
|
} finally {
|
|
speichernBtn.disabled = false;
|
|
}
|
|
});
|
|
|
|
modal.oeffnen();
|
|
}
|
|
|
|
async function loescheAgent(agentId, name) {
|
|
if (!confirm(t("agents.deleteConfirm", { name, id: agentId }))) return;
|
|
|
|
try {
|
|
await api(`/agents/${encodeURIComponent(agentId)}`, { method: "DELETE" });
|
|
zeigeMeldung(t("agents.deleted", { name }), "info");
|
|
await ladeUndRendereListe();
|
|
} catch (err) {
|
|
zeigeMeldung(t("agents.deleteFailed", { error: err.message }), "gefahr");
|
|
}
|
|
}
|
|
|
|
async function zeigeWerkzeugEinstellungen(agentId, toolName) {
|
|
const box = erstelleElement("div", { klasse: "einstellungen-box" });
|
|
box.append(erstelleElement("p", { klasse: "gedaempft", text: t("agents.loadingSettings") }));
|
|
|
|
const schliessenBtn = erstelleElement("button", {
|
|
type: "button",
|
|
klasse: "schaltflaeche flach",
|
|
onclick: () => modal.schliessen(),
|
|
text: t("common.close")
|
|
});
|
|
|
|
const modal = erstelleModal(
|
|
t("agents.settingsTitle", { tool: toolName, agent: agentId }),
|
|
box,
|
|
[schliessenBtn]
|
|
);
|
|
modal.oeffnen();
|
|
|
|
try {
|
|
const daten = await api(
|
|
`/agents/${encodeURIComponent(agentId)}/tools/${encodeURIComponent(toolName)}/settings`
|
|
);
|
|
box.replaceChildren();
|
|
|
|
const hinweis = erstelleElement("p", {
|
|
klasse: "hinweis",
|
|
text: t("agents.secretHint")
|
|
});
|
|
box.append(hinweis);
|
|
|
|
if (!daten || Object.keys(daten).length === 0) {
|
|
box.append(
|
|
erstelleElement("p", { klasse: "gedaempft", text: t("agents.noSettings") })
|
|
);
|
|
} else {
|
|
const dl = erstelleElement("dl", { klasse: "datenliste" });
|
|
for (const [schluessel, wert] of Object.entries(daten)) {
|
|
const dt = erstelleElement("dt", { text: schluessel });
|
|
const dd = erstelleElement("dd", { text: typeof wert === "object" ? JSON.stringify(wert) : String(wert) });
|
|
dl.append(dt, dd);
|
|
}
|
|
box.append(dl);
|
|
}
|
|
} catch (err) {
|
|
box.replaceChildren(
|
|
erstelleElement("p", { klasse: "fehler", text: t("agents.loadFailed", { error: err.message }) })
|
|
);
|
|
}
|
|
|
|
const rechteBox = erstelleElement("div", { klasse: "einstellungen-box" });
|
|
box.after(rechteBox);
|
|
await zeigeWerkzeugrechte(agentId, toolName, rechteBox);
|
|
}
|
|
|
|
function getWirkung(effective) {
|
|
const map = {
|
|
auto: [t("agents.effectAuto"), "erfolg"],
|
|
approve: [t("agents.effectApprove"), "warnung"],
|
|
deny: [t("agents.effectDeny"), "gefahr"]
|
|
};
|
|
return map[effective] ?? [effective, "neutral"];
|
|
}
|
|
|
|
/**
|
|
* #641: Rechte je Operation. "erlaubt" laeuft ohne Rueckfrage, "freigabe" wird einzeln
|
|
* vorgelegt, "verboten" gar nicht erst. Die Wirkung zeigt, was tatsaechlich geschieht -
|
|
* auch durch Dauerfreigaben oder die Vorgabe des Werkzeugs.
|
|
*/
|
|
async function zeigeWerkzeugrechte(agentId, toolName, box) {
|
|
const pfad = `/agents/${encodeURIComponent(agentId)}/tools/${encodeURIComponent(toolName)}/rights`;
|
|
let rechte;
|
|
try {
|
|
rechte = await api(pfad);
|
|
} catch (err) {
|
|
box.replaceChildren(erstelleElement("p", { klasse: "gedaempft", text: t("agents.loadFailed", { error: err.message }) }));
|
|
return;
|
|
}
|
|
|
|
box.replaceChildren(erstelleElement("h3", { text: t("agents.rights") }));
|
|
|
|
if (!rechte.operations || rechte.operations.length === 0) {
|
|
box.append(
|
|
erstelleElement("p", {
|
|
klasse: "gedaempft",
|
|
text: t("agents.noOperations")
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
|
|
box.append(
|
|
erstelleElement("p", {
|
|
klasse: "hinweis",
|
|
text: rechte.configured
|
|
? t("agents.configuredHint")
|
|
: t("agents.notConfiguredHint")
|
|
})
|
|
);
|
|
|
|
const tabelle = erstelleElement("table", { klasse: "daten-tabelle rechte-tabelle" });
|
|
const kopf = erstelleElement("tr", {});
|
|
for (const titel of [t("agents.operation"), t("agents.setting"), t("agents.effect")]) {
|
|
kopf.append(erstelleElement("th", { text: titel }));
|
|
}
|
|
tabelle.append(kopf);
|
|
|
|
const auswahl = new Map();
|
|
for (const op of rechte.operations) {
|
|
const select = erstelleElement(
|
|
"select",
|
|
{ disabled: op.alwaysApproval },
|
|
erstelleElement("option", { value: "erlaubt", text: t("agents.allowed") }),
|
|
erstelleElement("option", { value: "freigabe", text: t("agents.singleApproval") }),
|
|
erstelleElement("option", { value: "verboten", text: t("agents.forbidden") })
|
|
);
|
|
// Ohne gesetzte Rechte die tatsaechliche Wirkung vorbelegen - sonst wuerde das erste
|
|
// Speichern z.B. das freie Lesen still in eine Freigabepflicht verwandeln.
|
|
select.value = rechte.configured
|
|
? op.setting
|
|
: op.effective === "auto" ? "erlaubt" : op.effective === "deny" ? "verboten" : "freigabe";
|
|
auswahl.set(op.operation, select);
|
|
|
|
const [wirkungText, wirkungArt] = getWirkung(op.effective);
|
|
const wirkung = erstelleElement("td", {}, erstellePlakette(wirkungText, wirkungArt));
|
|
if (op.standingApprovalId) {
|
|
wirkung.append(" ", erstellePlakette(t("agents.standingApproval", { id: op.standingApprovalId }), "neutral"));
|
|
}
|
|
wirkung.append(erstelleElement("div", {
|
|
klasse: "grund",
|
|
text: op.alwaysApproval ? t("agents.alwaysApprovalReason") : op.reason
|
|
}));
|
|
|
|
const zeile = erstelleElement("tr", {});
|
|
zeile.append(erstelleElement("td", { text: op.operation }), erstelleElement("td", {}, select), wirkung);
|
|
tabelle.append(zeile);
|
|
}
|
|
box.append(tabelle);
|
|
|
|
const speichern = erstelleElement("button", {
|
|
type: "button",
|
|
klasse: "schaltflaeche primaer klein",
|
|
text: t("agents.saveRights"),
|
|
onclick: async () => {
|
|
const allowed = [];
|
|
const denied = [];
|
|
for (const [operation, select] of auswahl) {
|
|
if (select.disabled) continue;
|
|
if (select.value === "erlaubt") allowed.push(operation);
|
|
if (select.value === "verboten") denied.push(operation);
|
|
}
|
|
speichern.disabled = true;
|
|
try {
|
|
const res = await api(pfad, { method: "PUT", body: { allowed, denied } });
|
|
zeigeMeldung(res?.message ?? t("agents.rightsSaved"), "erfolg");
|
|
await zeigeWerkzeugrechte(agentId, toolName, box);
|
|
} catch (err) {
|
|
zeigeMeldung(t("agents.saveFailed", { error: err.message }), "gefahr");
|
|
speichern.disabled = false;
|
|
}
|
|
}
|
|
});
|
|
box.append(speichern);
|
|
}
|