Lumi/plugins/lumi_ai/backend/tool_registry.js
2026-06-13 20:28:06 +02:00

74 lines
2.8 KiB
JavaScript

const { roleAllows } = require("./permissions");
const ROLE_RANK = Object.freeze({ user: 1, mod: 2, admin: 3 });
const FORBIDDEN_DEFINITION_KEYS = Object.freeze([
"shell",
"sql",
"filesystem",
"network",
"code_execution",
"eval"
]);
function registerManagedTool(registry, metadata, definition) {
validateManagedDefinition(metadata, definition);
const metadataRole = metadataRequiredRole(metadata.permissions);
const backendRole = normalizeRole(definition.required_role);
const requiredRole = stricterRole(metadataRole, backendRole);
const backendPermissionCheck = definition.permission_check;
const mutating = definition.mutating === true ||
["sensitive", "high", "destructive"].includes(String(definition.risk_level || metadata.risk_level || "").toLowerCase());
return registry.register({
...definition,
owning_plugin: metadata.tool_id,
required_role: requiredRole,
required_permission: String(definition.required_permission || `${metadata.tool_id}.use`),
audit_category: String(definition.audit_category || metadata.tool_type || "ai_tool"),
confirmation_required: mutating || metadata.confirmation_required === true
? true
: definition.confirmation_required !== false,
permission_check: (input) => {
const actualRole = input.user?.isAdmin ? "admin" : input.user?.isMod ? "mod" : "user";
if (!roleAllows(actualRole, requiredRole)) return false;
return backendPermissionCheck(input) === true;
}
});
}
function validateManagedDefinition(metadata, definition) {
if (!definition || typeof definition !== "object") throw new Error("AI tool definition is required.");
if (!String(definition.tool_id || "").startsWith(`${metadata.tool_id}.`)) {
throw new Error(`Registered tool IDs must use the ${metadata.tool_id}. namespace.`);
}
if (typeof definition.permission_check !== "function" || typeof definition.workflow_handler !== "function") {
throw new Error("Managed AI tools require backend permission and workflow handlers.");
}
for (const key of FORBIDDEN_DEFINITION_KEYS) {
if (definition[key] != null) throw new Error(`Managed AI tools cannot request generic ${key} access.`);
}
}
function metadataRequiredRole(permissions) {
if (typeof permissions === "string") return normalizeRole(permissions);
if (Array.isArray(permissions)) {
return permissions.map(normalizeRole).sort((a, b) => ROLE_RANK[b] - ROLE_RANK[a])[0] || "user";
}
return normalizeRole(permissions?.required_role || permissions?.role);
}
function normalizeRole(value) {
return Object.hasOwn(ROLE_RANK, value) ? value : "user";
}
function stricterRole(left, right) {
return ROLE_RANK[left] >= ROLE_RANK[right] ? left : right;
}
module.exports = {
FORBIDDEN_DEFINITION_KEYS,
registerManagedTool,
validateManagedDefinition,
metadataRequiredRole,
stricterRole
};