From 967ecddfed873b78ec935ba2736c848bab0beb12 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 7 Apr 2026 11:04:02 +0100 Subject: [PATCH] refactor: dedupe extension lower readers --- extensions/nextcloud-talk/src/accounts.ts | 2 +- extensions/phone-control/index.ts | 3 ++- extensions/speech-core/src/tts.ts | 4 +++- extensions/talk-voice/index.ts | 9 +++++++-- src/agents/tools/image-tool.helpers.ts | 3 ++- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/extensions/nextcloud-talk/src/accounts.ts b/extensions/nextcloud-talk/src/accounts.ts index 0fcfade2f98..a910196e2d1 100644 --- a/extensions/nextcloud-talk/src/accounts.ts +++ b/extensions/nextcloud-talk/src/accounts.ts @@ -11,7 +11,7 @@ import { normalizeResolvedSecretInputString } from "./secret-input.js"; import type { CoreConfig, NextcloudTalkAccountConfig } from "./types.js"; function isTruthyEnvValue(value?: string): boolean { - const normalized = (value ?? "").trim().toLowerCase(); + const normalized = normalizeOptionalString(value)?.toLowerCase() ?? ""; return normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on"; } diff --git a/extensions/phone-control/index.ts b/extensions/phone-control/index.ts index fcf94c6ba97..a9f7c4a0c7c 100644 --- a/extensions/phone-control/index.ts +++ b/extensions/phone-control/index.ts @@ -1,5 +1,6 @@ import fs from "node:fs/promises"; import path from "node:path"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { definePluginEntry, type OpenClawPluginApi, @@ -259,7 +260,7 @@ function formatHelp(): string { } function parseGroup(raw: string | undefined): ArmGroup | null { - const value = (raw ?? "").trim().toLowerCase(); + const value = normalizeOptionalString(raw)?.toLowerCase() ?? ""; if (!value) { return null; } diff --git a/extensions/speech-core/src/tts.ts b/extensions/speech-core/src/tts.ts index f6aade8c2c4..a9f840c9e77 100644 --- a/extensions/speech-core/src/tts.ts +++ b/extensions/speech-core/src/tts.ts @@ -327,7 +327,9 @@ export function resolveTtsConfig(cfg: OpenClawConfig): ResolvedTtsConfig { mode: raw.mode ?? "final", provider: normalizeConfiguredSpeechProviderId(raw.provider) ?? - (providerSource === "config" ? raw.provider?.trim().toLowerCase() || "" : ""), + (providerSource === "config" + ? (normalizeOptionalString(raw.provider)?.toLowerCase() ?? "") + : ""), providerSource, summaryModel: normalizeOptionalString(raw.summaryModel), modelOverrides: resolveModelOverridePolicy(raw.modelOverrides), diff --git a/extensions/talk-voice/index.ts b/extensions/talk-voice/index.ts index 3cc93ca5f00..00cf64507f7 100644 --- a/extensions/talk-voice/index.ts +++ b/extensions/talk-voice/index.ts @@ -1,6 +1,7 @@ import { resolveActiveTalkProviderConfig } from "openclaw/plugin-sdk/config-runtime"; import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import type { SpeechVoiceOption } from "openclaw/plugin-sdk/speech"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { definePluginEntry, type OpenClawPluginApi } from "./api.js"; function mask(s: string, keep: number = 6): string { @@ -79,11 +80,15 @@ function findVoice(voices: SpeechVoiceOption[], query: string): SpeechVoiceOptio if (byId) { return byId; } - const exactName = voices.find((v) => (v.name ?? "").trim().toLowerCase() === lower); + const exactName = voices.find( + (v) => (normalizeOptionalString(v.name)?.toLowerCase() ?? "") === lower, + ); if (exactName) { return exactName; } - const partial = voices.find((v) => (v.name ?? "").trim().toLowerCase().includes(lower)); + const partial = voices.find((v) => + (normalizeOptionalString(v.name)?.toLowerCase() ?? "").includes(lower), + ); return partial ?? null; } diff --git a/src/agents/tools/image-tool.helpers.ts b/src/agents/tools/image-tool.helpers.ts index e72f0edcd1b..2fce5b5fc0c 100644 --- a/src/agents/tools/image-tool.helpers.ts +++ b/src/agents/tools/image-tool.helpers.ts @@ -1,5 +1,6 @@ import type { AssistantMessage } from "@mariozechner/pi-ai"; import type { OpenClawConfig } from "../../config/config.js"; +import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js"; import { findNormalizedProviderValue } from "../model-selection.js"; import { extractAssistantText } from "../pi-embedded-utils.js"; import { coerceToolModelConfig, type ToolModelConfig } from "./model-config.helpers.js"; @@ -16,7 +17,7 @@ export function decodeDataUrl(dataUrl: string): { if (!match) { throw new Error("Invalid data URL (expected base64 data: URL)."); } - const mimeType = (match[1] ?? "").trim().toLowerCase(); + const mimeType = normalizeLowercaseStringOrEmpty(match[1]); if (!mimeType.startsWith("image/")) { throw new Error(`Unsupported data URL type: ${mimeType || "unknown"}`); }