From e87300e2f46970f4f603fdcdc944825ea6d2f5b9 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 6 Apr 2026 23:48:24 +0100 Subject: [PATCH] refactor: dedupe config io record helper --- src/config/io.ts | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/config/io.ts b/src/config/io.ts index 84f6b74282c..82e82edcfc7 100644 --- a/src/config/io.ts +++ b/src/config/io.ts @@ -19,6 +19,7 @@ import { listPluginDoctorLegacyConfigRules, } from "../plugins/doctor-contract-registry.js"; import { sanitizeTerminalText } from "../terminal/safe-text.js"; +import { isRecord } from "../utils.js"; import { VERSION } from "../version.js"; import { DuplicateAgentDirError, findDuplicateAgentDirs } from "./agent-dirs.js"; import { maintainConfigBackups } from "./backup-rotation.js"; @@ -430,24 +431,20 @@ function coerceConfig(value: unknown): OpenClawConfig { return value as OpenClawConfig; } -function isPlainObject(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); -} - function hasConfigMeta(value: unknown): boolean { - if (!isPlainObject(value)) { + if (!isRecord(value)) { return false; } const meta = value.meta; - return isPlainObject(meta); + return isRecord(meta); } function resolveGatewayMode(value: unknown): string | null { - if (!isPlainObject(value)) { + if (!isRecord(value)) { return null; } const gateway = value.gateway; - if (!isPlainObject(gateway) || typeof gateway.mode !== "string") { + if (!isRecord(gateway) || typeof gateway.mode !== "string") { return null; } const trimmed = gateway.mode.trim(); @@ -459,7 +456,7 @@ function cloneUnknown(value: T): T { } function createMergePatch(base: unknown, target: unknown): unknown { - if (!isPlainObject(base) || !isPlainObject(target)) { + if (!isRecord(base) || !isRecord(target)) { return cloneUnknown(target); } @@ -478,9 +475,9 @@ function createMergePatch(base: unknown, target: unknown): unknown { continue; } const baseValue = base[key]; - if (isPlainObject(baseValue) && isPlainObject(targetValue)) { + if (isRecord(baseValue) && isRecord(targetValue)) { const childPatch = createMergePatch(baseValue, targetValue); - if (isPlainObject(childPatch) && Object.keys(childPatch).length === 0) { + if (isRecord(childPatch) && Object.keys(childPatch).length === 0) { continue; } patch[key] = childPatch; @@ -494,7 +491,7 @@ function createMergePatch(base: unknown, target: unknown): unknown { } function projectSourceOntoRuntimeShape(source: unknown, runtime: unknown): unknown { - if (!isPlainObject(source) || !isPlainObject(runtime)) { + if (!isRecord(source) || !isRecord(runtime)) { return cloneUnknown(source); } @@ -521,7 +518,7 @@ function collectEnvRefPaths(value: unknown, path: string, output: Map = {}; for (const [key, child] of Object.entries(value)) { @@ -735,7 +732,7 @@ async function readConfigHealthState(deps: Required): Promise): ConfigHealthSt const healthPath = resolveConfigHealthStatePath(deps.env, deps.homedir); const raw = deps.fs.readFileSync(healthPath, "utf-8"); const parsed = JSON.parse(raw); - return isPlainObject(parsed) ? (parsed as ConfigHealthState) : {}; + return isRecord(parsed) ? (parsed as ConfigHealthState) : {}; } catch { return {}; } @@ -783,11 +780,11 @@ function writeConfigHealthStateSync(deps: Required, state: ConfigH function getConfigHealthEntry(state: ConfigHealthState, configPath: string): ConfigHealthEntry { const entries = state.entries; - if (!entries || !isPlainObject(entries)) { + if (!entries || !isRecord(entries)) { return {}; } const entry = entries[configPath]; - return entry && isPlainObject(entry) ? entry : {}; + return entry && isRecord(entry) ? entry : {}; } function setConfigHealthEntry( @@ -805,7 +802,7 @@ function setConfigHealthEntry( } function isUpdateChannelOnlyRoot(value: unknown): boolean { - if (!isPlainObject(value)) { + if (!isRecord(value)) { return false; } const keys = Object.keys(value); @@ -813,7 +810,7 @@ function isUpdateChannelOnlyRoot(value: unknown): boolean { return false; } const update = value.update; - if (!isPlainObject(update)) { + if (!isRecord(update)) { return false; } const updateKeys = Object.keys(update);