mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 08:01:40 +00:00
refactor(shared): dedupe config path eval
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
import fs from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
import type { OpenClawConfig, SkillConfig } from "../../config/config.js";
|
import type { OpenClawConfig, SkillConfig } from "../../config/config.js";
|
||||||
import type { SkillEligibilityContext, SkillEntry } from "./types.js";
|
import type { SkillEligibilityContext, SkillEntry } from "./types.js";
|
||||||
|
import {
|
||||||
|
hasBinary,
|
||||||
|
isConfigPathTruthyWithDefaults,
|
||||||
|
resolveConfigPath,
|
||||||
|
resolveRuntimePlatform,
|
||||||
|
} from "../../shared/config-eval.js";
|
||||||
import { resolveSkillKey } from "./frontmatter.js";
|
import { resolveSkillKey } from "./frontmatter.js";
|
||||||
|
|
||||||
const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
|
const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
|
||||||
@@ -9,40 +13,10 @@ const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
|
|||||||
"browser.evaluateEnabled": true,
|
"browser.evaluateEnabled": true,
|
||||||
};
|
};
|
||||||
|
|
||||||
function isTruthy(value: unknown): boolean {
|
export { hasBinary, resolveConfigPath, resolveRuntimePlatform };
|
||||||
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 function isConfigPathTruthy(config: OpenClawConfig | undefined, pathStr: string): boolean {
|
export function isConfigPathTruthy(config: OpenClawConfig | undefined, pathStr: string): boolean {
|
||||||
const value = resolveConfigPath(config, pathStr);
|
return isConfigPathTruthyWithDefaults(config, pathStr, DEFAULT_CONFIG_VALUES);
|
||||||
if (value === undefined && pathStr in DEFAULT_CONFIG_VALUES) {
|
|
||||||
return DEFAULT_CONFIG_VALUES[pathStr];
|
|
||||||
}
|
|
||||||
return isTruthy(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveSkillConfig(
|
export function resolveSkillConfig(
|
||||||
@@ -60,10 +34,6 @@ export function resolveSkillConfig(
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveRuntimePlatform(): string {
|
|
||||||
return process.platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeAllowlist(input: unknown): string[] | undefined {
|
function normalizeAllowlist(input: unknown): string[] | undefined {
|
||||||
if (!input) {
|
if (!input) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -96,29 +66,6 @@ export function isBundledSkillAllowed(entry: SkillEntry, allowlist?: string[]):
|
|||||||
return allowlist.includes(key) || allowlist.includes(entry.skill.name);
|
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: {
|
export function shouldIncludeSkill(params: {
|
||||||
entry: SkillEntry;
|
entry: SkillEntry;
|
||||||
config?: OpenClawConfig;
|
config?: OpenClawConfig;
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import fs from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
import type { OpenClawConfig, HookConfig } from "../config/config.js";
|
import type { OpenClawConfig, HookConfig } from "../config/config.js";
|
||||||
import type { HookEligibilityContext, HookEntry } from "./types.js";
|
import type { HookEligibilityContext, HookEntry } from "./types.js";
|
||||||
|
import {
|
||||||
|
hasBinary,
|
||||||
|
isConfigPathTruthyWithDefaults,
|
||||||
|
resolveConfigPath,
|
||||||
|
resolveRuntimePlatform,
|
||||||
|
} from "../shared/config-eval.js";
|
||||||
import { resolveHookKey } from "./frontmatter.js";
|
import { resolveHookKey } from "./frontmatter.js";
|
||||||
|
|
||||||
const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
|
const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
|
||||||
@@ -10,40 +14,10 @@ const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
|
|||||||
"workspace.dir": true,
|
"workspace.dir": true,
|
||||||
};
|
};
|
||||||
|
|
||||||
function isTruthy(value: unknown): boolean {
|
export { hasBinary, resolveConfigPath, resolveRuntimePlatform };
|
||||||
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 function isConfigPathTruthy(config: OpenClawConfig | undefined, pathStr: string): boolean {
|
export function isConfigPathTruthy(config: OpenClawConfig | undefined, pathStr: string): boolean {
|
||||||
const value = resolveConfigPath(config, pathStr);
|
return isConfigPathTruthyWithDefaults(config, pathStr, DEFAULT_CONFIG_VALUES);
|
||||||
if (value === undefined && pathStr in DEFAULT_CONFIG_VALUES) {
|
|
||||||
return DEFAULT_CONFIG_VALUES[pathStr];
|
|
||||||
}
|
|
||||||
return isTruthy(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveHookConfig(
|
export function resolveHookConfig(
|
||||||
@@ -61,31 +35,6 @@ export function resolveHookConfig(
|
|||||||
return entry;
|
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: {
|
export function shouldIncludeHook(params: {
|
||||||
entry: HookEntry;
|
entry: HookEntry;
|
||||||
config?: OpenClawConfig;
|
config?: OpenClawConfig;
|
||||||
|
|||||||
71
src/shared/config-eval.ts
Normal file
71
src/shared/config-eval.ts
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user