refactor(shared): dedupe config path eval

This commit is contained in:
Peter Steinberger
2026-02-14 13:34:55 +00:00
parent b3882eccef
commit 25ecd4216c
3 changed files with 87 additions and 120 deletions

View File

@@ -1,7 +1,11 @@
import fs from "node:fs";
import path from "node:path";
import type { OpenClawConfig, SkillConfig } from "../../config/config.js";
import type { SkillEligibilityContext, SkillEntry } from "./types.js";
import {
hasBinary,
isConfigPathTruthyWithDefaults,
resolveConfigPath,
resolveRuntimePlatform,
} from "../../shared/config-eval.js";
import { resolveSkillKey } from "./frontmatter.js";
const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
@@ -9,40 +13,10 @@ const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
"browser.evaluateEnabled": true,
};
function isTruthy(value: unknown): boolean {
if (value === undefined || value === null) {
return false;
}
if (typeof value === "boolean") {
return value;
}
if (typeof value === "number") {
return value !== 0;
}
if (typeof value === "string") {
return value.trim().length > 0;
}
return true;
}
export function resolveConfigPath(config: OpenClawConfig | undefined, pathStr: string) {
const parts = pathStr.split(".").filter(Boolean);
let current: unknown = config;
for (const part of parts) {
if (typeof current !== "object" || current === null) {
return undefined;
}
current = (current as Record<string, unknown>)[part];
}
return current;
}
export { hasBinary, resolveConfigPath, resolveRuntimePlatform };
export function isConfigPathTruthy(config: OpenClawConfig | undefined, pathStr: string): boolean {
const value = resolveConfigPath(config, pathStr);
if (value === undefined && pathStr in DEFAULT_CONFIG_VALUES) {
return DEFAULT_CONFIG_VALUES[pathStr];
}
return isTruthy(value);
return isConfigPathTruthyWithDefaults(config, pathStr, DEFAULT_CONFIG_VALUES);
}
export function resolveSkillConfig(
@@ -60,10 +34,6 @@ export function resolveSkillConfig(
return entry;
}
export function resolveRuntimePlatform(): string {
return process.platform;
}
function normalizeAllowlist(input: unknown): string[] | undefined {
if (!input) {
return undefined;
@@ -96,29 +66,6 @@ export function isBundledSkillAllowed(entry: SkillEntry, allowlist?: string[]):
return allowlist.includes(key) || allowlist.includes(entry.skill.name);
}
export function hasBinary(bin: string): boolean {
const pathEnv = process.env.PATH ?? "";
const parts = pathEnv.split(path.delimiter).filter(Boolean);
const winPathExt = process.env.PATHEXT;
const winExtensions =
winPathExt !== undefined
? winPathExt.split(";").filter(Boolean)
: [".EXE", ".CMD", ".BAT", ".COM"];
const extensions = process.platform === "win32" ? ["", ...winExtensions] : [""];
for (const part of parts) {
for (const ext of extensions) {
const candidate = path.join(part, bin + ext);
try {
fs.accessSync(candidate, fs.constants.X_OK);
return true;
} catch {
// keep scanning
}
}
}
return false;
}
export function shouldIncludeSkill(params: {
entry: SkillEntry;
config?: OpenClawConfig;

View File

@@ -1,7 +1,11 @@
import fs from "node:fs";
import path from "node:path";
import type { OpenClawConfig, HookConfig } from "../config/config.js";
import type { HookEligibilityContext, HookEntry } from "./types.js";
import {
hasBinary,
isConfigPathTruthyWithDefaults,
resolveConfigPath,
resolveRuntimePlatform,
} from "../shared/config-eval.js";
import { resolveHookKey } from "./frontmatter.js";
const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
@@ -10,40 +14,10 @@ const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
"workspace.dir": true,
};
function isTruthy(value: unknown): boolean {
if (value === undefined || value === null) {
return false;
}
if (typeof value === "boolean") {
return value;
}
if (typeof value === "number") {
return value !== 0;
}
if (typeof value === "string") {
return value.trim().length > 0;
}
return true;
}
export function resolveConfigPath(config: OpenClawConfig | undefined, pathStr: string) {
const parts = pathStr.split(".").filter(Boolean);
let current: unknown = config;
for (const part of parts) {
if (typeof current !== "object" || current === null) {
return undefined;
}
current = (current as Record<string, unknown>)[part];
}
return current;
}
export { hasBinary, resolveConfigPath, resolveRuntimePlatform };
export function isConfigPathTruthy(config: OpenClawConfig | undefined, pathStr: string): boolean {
const value = resolveConfigPath(config, pathStr);
if (value === undefined && pathStr in DEFAULT_CONFIG_VALUES) {
return DEFAULT_CONFIG_VALUES[pathStr];
}
return isTruthy(value);
return isConfigPathTruthyWithDefaults(config, pathStr, DEFAULT_CONFIG_VALUES);
}
export function resolveHookConfig(
@@ -61,31 +35,6 @@ export function resolveHookConfig(
return entry;
}
export function resolveRuntimePlatform(): string {
return process.platform;
}
export function hasBinary(bin: string): boolean {
const pathEnv = process.env.PATH ?? "";
const parts = pathEnv.split(path.delimiter).filter(Boolean);
const extensions =
process.platform === "win32"
? ["", ...(process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean)]
: [""];
for (const part of parts) {
for (const ext of extensions) {
const candidate = path.join(part, bin + ext);
try {
fs.accessSync(candidate, fs.constants.X_OK);
return true;
} catch {
// keep scanning
}
}
}
return false;
}
export function shouldIncludeHook(params: {
entry: HookEntry;
config?: OpenClawConfig;

71
src/shared/config-eval.ts Normal file
View File

@@ -0,0 +1,71 @@
import fs from "node:fs";
import path from "node:path";
export function isTruthy(value: unknown): boolean {
if (value === undefined || value === null) {
return false;
}
if (typeof value === "boolean") {
return value;
}
if (typeof value === "number") {
return value !== 0;
}
if (typeof value === "string") {
return value.trim().length > 0;
}
return true;
}
export function resolveConfigPath(config: unknown, pathStr: string): unknown {
const parts = pathStr.split(".").filter(Boolean);
let current: unknown = config;
for (const part of parts) {
if (typeof current !== "object" || current === null) {
return undefined;
}
current = (current as Record<string, unknown>)[part];
}
return current;
}
export function isConfigPathTruthyWithDefaults(
config: unknown,
pathStr: string,
defaults: Record<string, boolean>,
): boolean {
const value = resolveConfigPath(config, pathStr);
if (value === undefined && pathStr in defaults) {
return defaults[pathStr] ?? false;
}
return isTruthy(value);
}
export function resolveRuntimePlatform(): string {
return process.platform;
}
function windowsPathExtensions(): string[] {
const raw = process.env.PATHEXT;
const list =
raw !== undefined ? raw.split(";").map((v) => v.trim()) : [".EXE", ".CMD", ".BAT", ".COM"];
return ["", ...list.filter(Boolean)];
}
export function hasBinary(bin: string): boolean {
const pathEnv = process.env.PATH ?? "";
const parts = pathEnv.split(path.delimiter).filter(Boolean);
const extensions = process.platform === "win32" ? windowsPathExtensions() : [""];
for (const part of parts) {
for (const ext of extensions) {
const candidate = path.join(part, bin + ext);
try {
fs.accessSync(candidate, fs.constants.X_OK);
return true;
} catch {
// keep scanning
}
}
}
return false;
}