From 7687f6cfcd5c46e001dcc0badc00e22e15abd9c6 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 17 Feb 2026 00:44:47 +0000 Subject: [PATCH] refactor: reuse runtime requires evaluation --- src/agents/skills/config.ts | 64 +++++++++---------------------------- src/hooks/config.ts | 61 ++++++----------------------------- src/shared/config-eval.ts | 62 +++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 100 deletions(-) diff --git a/src/agents/skills/config.ts b/src/agents/skills/config.ts index 203c2fddfba..7662927e6c0 100644 --- a/src/agents/skills/config.ts +++ b/src/agents/skills/config.ts @@ -1,12 +1,13 @@ import type { OpenClawConfig, SkillConfig } from "../../config/config.js"; +import type { SkillEligibilityContext, SkillEntry } from "./types.js"; import { + evaluateRuntimeRequires, hasBinary, isConfigPathTruthyWithDefaults, resolveConfigPath, resolveRuntimePlatform, } from "../../shared/config-eval.js"; import { resolveSkillKey } from "./frontmatter.js"; -import type { SkillEligibilityContext, SkillEntry } from "./types.js"; const DEFAULT_CONFIG_VALUES: Record = { "browser.enabled": true, @@ -95,52 +96,17 @@ export function shouldIncludeSkill(params: { return true; } - const requiredBins = entry.metadata?.requires?.bins ?? []; - if (requiredBins.length > 0) { - for (const bin of requiredBins) { - if (hasBinary(bin)) { - continue; - } - if (eligibility?.remote?.hasBin?.(bin)) { - continue; - } - return false; - } - } - const requiredAnyBins = entry.metadata?.requires?.anyBins ?? []; - if (requiredAnyBins.length > 0) { - const anyFound = - requiredAnyBins.some((bin) => hasBinary(bin)) || - eligibility?.remote?.hasAnyBin?.(requiredAnyBins); - if (!anyFound) { - return false; - } - } - - const requiredEnv = entry.metadata?.requires?.env ?? []; - if (requiredEnv.length > 0) { - for (const envName of requiredEnv) { - if (process.env[envName]) { - continue; - } - if (skillConfig?.env?.[envName]) { - continue; - } - if (skillConfig?.apiKey && entry.metadata?.primaryEnv === envName) { - continue; - } - return false; - } - } - - const requiredConfig = entry.metadata?.requires?.config ?? []; - if (requiredConfig.length > 0) { - for (const configPath of requiredConfig) { - if (!isConfigPathTruthy(config, configPath)) { - return false; - } - } - } - - return true; + return evaluateRuntimeRequires({ + requires: entry.metadata?.requires, + hasBin: hasBinary, + hasRemoteBin: eligibility?.remote?.hasBin, + hasAnyRemoteBin: eligibility?.remote?.hasAnyBin, + hasEnv: (envName) => + Boolean( + process.env[envName] || + skillConfig?.env?.[envName] || + (skillConfig?.apiKey && entry.metadata?.primaryEnv === envName), + ), + isConfigPathTruthy: (configPath) => isConfigPathTruthy(config, configPath), + }); } diff --git a/src/hooks/config.ts b/src/hooks/config.ts index 4ee7e112d5f..df028027401 100644 --- a/src/hooks/config.ts +++ b/src/hooks/config.ts @@ -1,12 +1,13 @@ import type { OpenClawConfig, HookConfig } from "../config/config.js"; +import type { HookEligibilityContext, HookEntry } from "./types.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, @@ -66,54 +67,12 @@ export function shouldIncludeHook(params: { return true; } - // Check required binaries (all must be present) - const requiredBins = entry.metadata?.requires?.bins ?? []; - if (requiredBins.length > 0) { - for (const bin of requiredBins) { - if (hasBinary(bin)) { - continue; - } - if (eligibility?.remote?.hasBin?.(bin)) { - continue; - } - return false; - } - } - - // Check anyBins (at least one must be present) - const requiredAnyBins = entry.metadata?.requires?.anyBins ?? []; - if (requiredAnyBins.length > 0) { - const anyFound = - requiredAnyBins.some((bin) => hasBinary(bin)) || - eligibility?.remote?.hasAnyBin?.(requiredAnyBins); - if (!anyFound) { - return false; - } - } - - // Check required environment variables - const requiredEnv = entry.metadata?.requires?.env ?? []; - if (requiredEnv.length > 0) { - for (const envName of requiredEnv) { - if (process.env[envName]) { - continue; - } - if (hookConfig?.env?.[envName]) { - continue; - } - return false; - } - } - - // Check required config paths - const requiredConfig = entry.metadata?.requires?.config ?? []; - if (requiredConfig.length > 0) { - for (const configPath of requiredConfig) { - if (!isConfigPathTruthy(config, configPath)) { - return false; - } - } - } - - 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), + }); } diff --git a/src/shared/config-eval.ts b/src/shared/config-eval.ts index c1e4f6926a7..d11fccf7fd1 100644 --- a/src/shared/config-eval.ts +++ b/src/shared/config-eval.ts @@ -41,6 +41,68 @@ export function isConfigPathTruthyWithDefaults( return isTruthy(value); } +export type RuntimeRequires = { + bins?: string[]; + anyBins?: string[]; + env?: string[]; + config?: string[]; +}; + +export function evaluateRuntimeRequires(params: { + requires?: RuntimeRequires; + hasBin: (bin: string) => boolean; + hasAnyRemoteBin?: (bins: string[]) => boolean; + hasRemoteBin?: (bin: string) => boolean; + hasEnv: (envName: string) => boolean; + isConfigPathTruthy: (pathStr: string) => boolean; +}): boolean { + const requires = params.requires; + if (!requires) { + return true; + } + + const requiredBins = requires.bins ?? []; + if (requiredBins.length > 0) { + for (const bin of requiredBins) { + if (params.hasBin(bin)) { + continue; + } + if (params.hasRemoteBin?.(bin)) { + continue; + } + return false; + } + } + + const requiredAnyBins = requires.anyBins ?? []; + if (requiredAnyBins.length > 0) { + const anyFound = requiredAnyBins.some((bin) => params.hasBin(bin)); + if (!anyFound && !params.hasAnyRemoteBin?.(requiredAnyBins)) { + return false; + } + } + + const requiredEnv = requires.env ?? []; + if (requiredEnv.length > 0) { + for (const envName of requiredEnv) { + if (!params.hasEnv(envName)) { + return false; + } + } + } + + const requiredConfig = requires.config ?? []; + if (requiredConfig.length > 0) { + for (const configPath of requiredConfig) { + if (!params.isConfigPathTruthy(configPath)) { + return false; + } + } + } + + return true; +} + export function resolveRuntimePlatform(): string { return process.platform; }