import type { OpenClawConfig, HookConfig } from "../config/config.js"; import { evaluateRuntimeRequires, hasBinary, isConfigPathTruthyWithDefaults, resolveConfigPath, resolveRuntimePlatform, } from "../shared/config-eval.js"; import { resolveHookKey } from "./frontmatter.js"; import type { HookEligibilityContext, HookEntry } from "./types.js"; const DEFAULT_CONFIG_VALUES: Record = { "browser.enabled": true, "browser.evaluateEnabled": true, "workspace.dir": true, }; export { hasBinary, resolveConfigPath, resolveRuntimePlatform }; export function isConfigPathTruthy(config: OpenClawConfig | undefined, pathStr: string): boolean { return isConfigPathTruthyWithDefaults(config, pathStr, DEFAULT_CONFIG_VALUES); } export function resolveHookConfig( config: OpenClawConfig | undefined, hookKey: string, ): HookConfig | undefined { const hooks = config?.hooks?.internal?.entries; if (!hooks || typeof hooks !== "object") { return undefined; } const entry = (hooks as Record)[hookKey]; if (!entry || typeof entry !== "object") { return undefined; } return entry; } export function shouldIncludeHook(params: { entry: HookEntry; config?: OpenClawConfig; eligibility?: HookEligibilityContext; }): boolean { const { entry, config, eligibility } = params; const hookKey = resolveHookKey(entry.hook.name, entry); const hookConfig = resolveHookConfig(config, hookKey); const pluginManaged = entry.hook.source === "openclaw-plugin"; const osList = entry.metadata?.os ?? []; const remotePlatforms = eligibility?.remote?.platforms ?? []; // Check if explicitly disabled if (!pluginManaged && hookConfig?.enabled === false) { return false; } // Check OS requirement if ( osList.length > 0 && !osList.includes(resolveRuntimePlatform()) && !remotePlatforms.some((platform) => osList.includes(platform)) ) { return false; } // If marked as 'always', bypass all other checks if (entry.metadata?.always === true) { return true; } return evaluateRuntimeRequires({ requires: entry.metadata?.requires, hasBin: hasBinary, hasRemoteBin: eligibility?.remote?.hasBin, hasAnyRemoteBin: eligibility?.remote?.hasAnyBin, hasEnv: (envName) => Boolean(process.env[envName] || hookConfig?.env?.[envName]), isConfigPathTruthy: (configPath) => isConfigPathTruthy(config, configPath), }); }