Files
shumengya 50edff80f5 feat: 导出 SproutClaw .sproutclaw 配置
包含 extensions、skills、prompts、settings、auth、models、mcp 等配置。
排除 node_modules、npm 缓存、sessions 等运行时数据。
2026-06-26 15:48:56 +08:00

65 lines
1.9 KiB
TypeScript

import { ConsentError } from "./errors.ts";
import { logger } from "./logger.ts";
export type ToolConsentMode = "never" | "once-per-server" | "always";
export class ConsentManager {
private approvedServers = new Set<string>();
private deniedServers = new Set<string>();
private log = logger.child({ component: "ConsentManager" });
constructor(private mode: ToolConsentMode = "once-per-server") {
this.log.debug("Initialized", { mode });
}
requiresPrompt(serverName: string): boolean {
if (this.mode === "never") return false;
if (this.deniedServers.has(serverName)) return true;
if (this.mode === "always") return true;
return !this.approvedServers.has(serverName);
}
shouldCacheConsent(): boolean {
return this.mode !== "always";
}
registerDecision(serverName: string, approved: boolean): void {
this.deniedServers.delete(serverName);
this.approvedServers.delete(serverName);
if (approved) {
this.approvedServers.add(serverName);
this.log.debug("Consent granted", { server: serverName });
return;
}
this.deniedServers.add(serverName);
this.log.debug("Consent denied", { server: serverName });
}
ensureApproved(serverName: string): void {
if (this.mode === "never") return;
if (this.deniedServers.has(serverName)) {
throw new ConsentError(serverName, { denied: true });
}
if (!this.approvedServers.has(serverName)) {
throw new ConsentError(serverName, { requiresApproval: true });
}
if (this.mode === "always") {
this.approvedServers.delete(serverName);
}
}
clear(serverName?: string): void {
if (serverName) {
this.approvedServers.delete(serverName);
this.deniedServers.delete(serverName);
this.log.debug("Cleared consent for server", { server: serverName });
return;
}
this.approvedServers.clear();
this.deniedServers.clear();
this.log.debug("Cleared all consent records");
}
}