Files
openclaw/src/agents/skills/config.ts
2026-02-17 13:36:48 +09:00

113 lines
3.4 KiB
TypeScript

import type { OpenClawConfig, SkillConfig } from "../../config/config.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<string, boolean> = {
"browser.enabled": true,
"browser.evaluateEnabled": true,
};
export { hasBinary, resolveConfigPath, resolveRuntimePlatform };
export function isConfigPathTruthy(config: OpenClawConfig | undefined, pathStr: string): boolean {
return isConfigPathTruthyWithDefaults(config, pathStr, DEFAULT_CONFIG_VALUES);
}
export function resolveSkillConfig(
config: OpenClawConfig | undefined,
skillKey: string,
): SkillConfig | undefined {
const skills = config?.skills?.entries;
if (!skills || typeof skills !== "object") {
return undefined;
}
const entry = (skills as Record<string, SkillConfig | undefined>)[skillKey];
if (!entry || typeof entry !== "object") {
return undefined;
}
return entry;
}
function normalizeAllowlist(input: unknown): string[] | undefined {
if (!input) {
return undefined;
}
if (!Array.isArray(input)) {
return undefined;
}
const normalized = input.map((entry) => String(entry).trim()).filter(Boolean);
return normalized.length > 0 ? normalized : undefined;
}
const BUNDLED_SOURCES = new Set(["openclaw-bundled"]);
function isBundledSkill(entry: SkillEntry): boolean {
return BUNDLED_SOURCES.has(entry.skill.source);
}
export function resolveBundledAllowlist(config?: OpenClawConfig): string[] | undefined {
return normalizeAllowlist(config?.skills?.allowBundled);
}
export function isBundledSkillAllowed(entry: SkillEntry, allowlist?: string[]): boolean {
if (!allowlist || allowlist.length === 0) {
return true;
}
if (!isBundledSkill(entry)) {
return true;
}
const key = resolveSkillKey(entry.skill, entry);
return allowlist.includes(key) || allowlist.includes(entry.skill.name);
}
export function shouldIncludeSkill(params: {
entry: SkillEntry;
config?: OpenClawConfig;
eligibility?: SkillEligibilityContext;
}): boolean {
const { entry, config, eligibility } = params;
const skillKey = resolveSkillKey(entry.skill, entry);
const skillConfig = resolveSkillConfig(config, skillKey);
const allowBundled = normalizeAllowlist(config?.skills?.allowBundled);
const osList = entry.metadata?.os ?? [];
const remotePlatforms = eligibility?.remote?.platforms ?? [];
if (skillConfig?.enabled === false) {
return false;
}
if (!isBundledSkillAllowed(entry, allowBundled)) {
return false;
}
if (
osList.length > 0 &&
!osList.includes(resolveRuntimePlatform()) &&
!remotePlatforms.some((platform) => osList.includes(platform))
) {
return false;
}
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] ||
skillConfig?.env?.[envName] ||
(skillConfig?.apiKey && entry.metadata?.primaryEnv === envName),
),
isConfigPathTruthy: (configPath) => isConfigPathTruthy(config, configPath),
});
}