diff --git a/extensions/browser/src/browser/config.ts b/extensions/browser/src/browser/config.ts index f1367bc3b69..b5aee21b4f4 100644 --- a/extensions/browser/src/browser/config.ts +++ b/extensions/browser/src/browser/config.ts @@ -1,3 +1,4 @@ +import { normalizeTrimmedStringList } from "openclaw/plugin-sdk/text-runtime"; import { type BrowserConfig, type BrowserProfileConfig, @@ -116,9 +117,7 @@ function normalizeStringList(raw: string[] | undefined): string[] | undefined { if (!Array.isArray(raw) || raw.length === 0) { return undefined; } - const values = raw - .map((value) => value.trim()) - .filter((value): value is string => value.length > 0); + const values = normalizeTrimmedStringList(raw); return values.length > 0 ? values : undefined; } diff --git a/extensions/memory-wiki/src/markdown.ts b/extensions/memory-wiki/src/markdown.ts index c05c930363c..aeb32d2cc13 100644 --- a/extensions/memory-wiki/src/markdown.ts +++ b/extensions/memory-wiki/src/markdown.ts @@ -1,5 +1,8 @@ import path from "node:path"; -import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; +import { + normalizeOptionalString, + normalizeSingleOrTrimmedStringList, +} from "openclaw/plugin-sdk/text-runtime"; import YAML from "yaml"; export const WIKI_PAGE_KINDS = ["entity", "concept", "source", "synthesis", "report"] as const; @@ -82,23 +85,7 @@ export function extractTitleFromMarkdown(body: string): string | undefined { } export function normalizeSourceIds(value: unknown): string[] { - if (Array.isArray(value)) { - return value.flatMap((item) => (typeof item === "string" && item.trim() ? [item.trim()] : [])); - } - if (typeof value === "string" && value.trim()) { - return [value.trim()]; - } - return []; -} - -function normalizeStringList(value: unknown): string[] { - if (Array.isArray(value)) { - return value.flatMap((item) => (typeof item === "string" && item.trim() ? [item.trim()] : [])); - } - if (typeof value === "string" && value.trim()) { - return [value.trim()]; - } - return []; + return normalizeSingleOrTrimmedStringList(value); } export function extractWikiLinks(markdown: string): string[] { @@ -187,8 +174,8 @@ export function toWikiPageSummary(params: { pageType: normalizeOptionalString(parsed.frontmatter.pageType), sourceIds: normalizeSourceIds(parsed.frontmatter.sourceIds), linkTargets: extractWikiLinks(params.raw), - contradictions: normalizeStringList(parsed.frontmatter.contradictions), - questions: normalizeStringList(parsed.frontmatter.questions), + contradictions: normalizeSingleOrTrimmedStringList(parsed.frontmatter.contradictions), + questions: normalizeSingleOrTrimmedStringList(parsed.frontmatter.questions), confidence: typeof parsed.frontmatter.confidence === "number" && Number.isFinite(parsed.frontmatter.confidence) diff --git a/src/hooks/bundled/bootstrap-extra-files/handler.ts b/src/hooks/bundled/bootstrap-extra-files/handler.ts index bc708b62d07..741190baa7b 100644 --- a/src/hooks/bundled/bootstrap-extra-files/handler.ts +++ b/src/hooks/bundled/bootstrap-extra-files/handler.ts @@ -3,29 +3,23 @@ import { loadExtraBootstrapFilesWithDiagnostics, } from "../../../agents/workspace.js"; import { createSubsystemLogger } from "../../../logging/subsystem.js"; +import { normalizeTrimmedStringList } from "../../../shared/string-normalization.js"; import { resolveHookConfig } from "../../config.js"; import { isAgentBootstrapEvent, type HookHandler } from "../../hooks.js"; const HOOK_KEY = "bootstrap-extra-files"; const log = createSubsystemLogger("bootstrap-extra-files"); -function normalizeStringArray(value: unknown): string[] { - if (!Array.isArray(value)) { - return []; - } - return value.map((v) => (typeof v === "string" ? v.trim() : "")).filter(Boolean); -} - function resolveExtraBootstrapPatterns(hookConfig: Record): string[] { - const fromPaths = normalizeStringArray(hookConfig.paths); + const fromPaths = normalizeTrimmedStringList(hookConfig.paths); if (fromPaths.length > 0) { return fromPaths; } - const fromPatterns = normalizeStringArray(hookConfig.patterns); + const fromPatterns = normalizeTrimmedStringList(hookConfig.patterns); if (fromPatterns.length > 0) { return fromPatterns; } - return normalizeStringArray(hookConfig.files); + return normalizeTrimmedStringList(hookConfig.files); } const bootstrapExtraFilesHook: HookHandler = async (event) => { diff --git a/src/infra/node-pairing.ts b/src/infra/node-pairing.ts index b4fac6a11a6..e34ddefb179 100644 --- a/src/infra/node-pairing.ts +++ b/src/infra/node-pairing.ts @@ -1,5 +1,6 @@ import { randomUUID } from "node:crypto"; import { resolveMissingRequestedScope } from "../shared/operator-scope-compat.js"; +import { normalizeTrimmedStringList } from "../shared/string-normalization.js"; import { type NodeApprovalScope, resolveNodePairApprovalScopes } from "./node-pairing-authz.js"; import { createAsyncLock, @@ -70,7 +71,7 @@ function normalizeStringList(values?: string[]): string[] | undefined { if (!Array.isArray(values)) { return undefined; } - const normalized = values.map((value) => value.trim()).filter(Boolean); + const normalized = normalizeTrimmedStringList(values); return normalized.length > 0 ? normalized : []; } diff --git a/src/shared/frontmatter.ts b/src/shared/frontmatter.ts index ce042b18762..998dd086dbb 100644 --- a/src/shared/frontmatter.ts +++ b/src/shared/frontmatter.ts @@ -1,21 +1,10 @@ import JSON5 from "json5"; import { LEGACY_MANIFEST_KEYS, MANIFEST_KEY } from "../compat/legacy-names.js"; import { parseBooleanValue } from "../utils/boolean.js"; +import { normalizeCsvOrLooseStringList } from "./string-normalization.js"; export function normalizeStringList(input: unknown): string[] { - if (!input) { - return []; - } - if (Array.isArray(input)) { - return input.map((value) => String(value).trim()).filter(Boolean); - } - if (typeof input === "string") { - return input - .split(",") - .map((value) => value.trim()) - .filter(Boolean); - } - return []; + return normalizeCsvOrLooseStringList(input); } export function getFrontmatterString( diff --git a/src/shared/string-normalization.ts b/src/shared/string-normalization.ts index 733b5e02166..65c7576741e 100644 --- a/src/shared/string-normalization.ts +++ b/src/shared/string-normalization.ts @@ -25,6 +25,19 @@ export function normalizeSingleOrTrimmedStringList(value: unknown): string[] { return []; } +export function normalizeCsvOrLooseStringList(value: unknown): string[] { + if (Array.isArray(value)) { + return value.map((entry) => String(entry).trim()).filter(Boolean); + } + if (typeof value === "string") { + return value + .split(",") + .map((entry) => entry.trim()) + .filter(Boolean); + } + return []; +} + export function normalizeHyphenSlug(raw?: string | null) { const trimmed = raw?.trim().toLowerCase() ?? ""; if (!trimmed) {