import { buildSchemaUrl, ConfigLoader } from "@aliou/pi-utils-settings"; import pkg from "../../../package.json" with { type: "json" }; import type { AllowedPath } from "../../core/paths/path"; import { DEFAULT_CONFIG } from "./defaults"; import type { GuardrailsConfig, PolicyRule, ResolvedConfig, WorkspaceRootEntry, } from "./types"; export function createGuardrailsConfigLoader(): ConfigLoader< GuardrailsConfig, ResolvedConfig > { return new ConfigLoader("guardrails", DEFAULT_CONFIG, { scopes: ["global", "local", "memory"], schemaUrl: buildSchemaUrl(pkg.name, pkg.version), afterMerge: (resolved, global, local, memory) => { const ruleMap = new Map(); if (resolved.applyBuiltinDefaults) { for (const rule of DEFAULT_CONFIG.policies.rules) { ruleMap.set(rule.id, rule); } } if (global?.policies?.rules) { for (const rule of global.policies.rules) { ruleMap.set(rule.id, rule); } } if (local?.policies?.rules) { for (const rule of local.policies.rules) { ruleMap.set(rule.id, rule); } } if (memory?.policies?.rules) { for (const rule of memory.policies.rules) { ruleMap.set(rule.id, rule); } } resolved.policies.rules = [...ruleMap.values()]; const customPatterns = memory?.permissionGate?.customPatterns ?? local?.permissionGate?.customPatterns ?? global?.permissionGate?.customPatterns; if (customPatterns) { resolved.permissionGate.patterns = customPatterns; resolved.permissionGate.useBuiltinMatchers = false; } const mergedPaths = new Map(); for (const paths of [ DEFAULT_CONFIG.pathAccess.allowedPaths, global?.pathAccess?.allowedPaths, local?.pathAccess?.allowedPaths, memory?.pathAccess?.allowedPaths, ]) { for (const entry of paths ?? []) { if (!entry || typeof entry !== "object") continue; const path = typeof entry.path === "string" ? entry.path.trim() : ""; if (!path) continue; const kind = entry.kind === "directory" ? "directory" : "file"; mergedPaths.set(`${kind}:${path}`, { kind, path }); } } resolved.pathAccess.allowedPaths = [...mergedPaths.values()]; const mergedRoots = new Map(); for (const roots of [ global?.workspaceRoots?.roots, local?.workspaceRoots?.roots, memory?.workspaceRoots?.roots, ]) { for (const entry of roots ?? []) { if (!entry || typeof entry !== "object") continue; const path = typeof entry.path === "string" ? entry.path.trim() : ""; if (!path) continue; mergedRoots.set(path, { path, ...(typeof entry.alias === "string" && entry.alias.trim() ? { alias: entry.alias.trim() } : {}), }); } } resolved.workspaceRoots.roots = [...mergedRoots.values()]; resolved.audit = { ...DEFAULT_CONFIG.audit, ...global?.audit, ...local?.audit, ...memory?.audit, }; return resolved; }, }); } export const configLoader = createGuardrailsConfigLoader();