UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

73 lines (72 loc) 3.77 kB
//#region src/config/recovery-policy.ts const PLUGIN_ENTRY_PATH_PREFIX = "plugins.entries."; const PLUGIN_POLICY_PATHS = new Set(["plugins.allow", "plugins.deny"]); const COMPILED_RUNTIME_OUTPUT_DIAGNOSTIC = "compiled runtime output"; const PLUGIN_DIAGNOSTIC_PREFIX_PATTERN = /^plugin\s+([^:\s]+):\s/u; const PLUGIN_NOT_FOUND_PATTERN = /^plugin not found:\s*([^\s(]+)/u; function isPluginsPath(path) { return path === "plugins" || path.startsWith("plugins."); } function isPluginEntryIssue(issue) { const path = issue.path.trim(); if (!path.startsWith(PLUGIN_ENTRY_PATH_PREFIX)) return false; return path.slice(16).trim().length > 0; } function isPluginPolicyIssue(issue) { return PLUGIN_POLICY_PATHS.has(issue.path.trim()) && issue.message.trim().startsWith("plugin not found:"); } /** Return true for plugin validation issues caused by missing compiled runtime output. */ function isPluginPackagingRuntimeOutputIssue(issue) { const path = issue.path.trim(); const message = issue.message.trim().toLowerCase(); return isPluginsPath(path) && message.includes(COMPILED_RUNTIME_OUTPUT_DIAGNOSTIC); } function isPluginPackagingFalloutIssue(issue) { const path = issue.path.trim(); const message = issue.message.trim(); return isPluginsPath(path) && message.startsWith("plugin not found:"); } function normalizePluginIssueId(value) { const normalized = value?.trim().toLowerCase(); return normalized ? normalized : null; } function extractPluginPackagingRuntimeOutputPluginId(issue) { if (!isPluginPackagingRuntimeOutputIssue(issue)) return null; return normalizePluginIssueId(PLUGIN_DIAGNOSTIC_PREFIX_PATTERN.exec(issue.message.trim())?.[1]); } function extractPluginNotFoundIssuePluginId(issue) { if (!isPluginPackagingFalloutIssue(issue)) return null; return normalizePluginIssueId(PLUGIN_NOT_FOUND_PATTERN.exec(issue.message.trim())?.[1]); } /** * Return true when an invalid config snapshot is blocked only by plugin packaging fallout. * This lets callers show plugin repair hints instead of treating user config as corrupted. */ function isPluginPackagingRuntimeOutputInvalidConfigSnapshot(snapshot) { if (snapshot.valid || (snapshot.legacyIssues?.length ?? 0) > 0 || snapshot.issues.length === 0) return false; const packagingIssues = [...snapshot.issues, ...snapshot.warnings ?? []].filter(isPluginPackagingRuntimeOutputIssue); const packagingPluginIds = new Set(packagingIssues.map((issue) => extractPluginPackagingRuntimeOutputPluginId(issue)).filter((pluginId) => pluginId !== null)); return packagingIssues.length > 0 && snapshot.issues.every((issue) => { if (isPluginPackagingRuntimeOutputIssue(issue)) return true; const pluginId = extractPluginNotFoundIssuePluginId(issue); return pluginId !== null && packagingPluginIds.has(pluginId); }); } /** * Return true when an invalid config snapshot is scoped entirely to stale plugin refs. * Whole-file recovery is skipped for these snapshots so plugin cleanup can preserve user config. */ function isPluginLocalInvalidConfigSnapshot(snapshot) { if (snapshot.valid || snapshot.legacyIssues.length > 0 || snapshot.issues.length === 0) return false; return snapshot.issues.every((issue) => isPluginEntryIssue(issue) || isPluginPolicyIssue(issue)); } /** * Decide whether whole-file last-known-good recovery is appropriate for an invalid snapshot. * Plugin-local failures stay on the current file so targeted plugin cleanup can run. */ function shouldAttemptLastKnownGoodRecovery(snapshot) { if (snapshot.valid) return false; return !isPluginLocalInvalidConfigSnapshot(snapshot); } //#endregion export { shouldAttemptLastKnownGoodRecovery as i, isPluginPackagingRuntimeOutputInvalidConfigSnapshot as n, isPluginPackagingRuntimeOutputIssue as r, isPluginLocalInvalidConfigSnapshot as t };