diff --git a/src/agents/tools/web-fetch.ts b/src/agents/tools/web-fetch.ts index cbdbabfc766..1b98b9928fc 100644 --- a/src/agents/tools/web-fetch.ts +++ b/src/agents/tools/web-fetch.ts @@ -4,6 +4,7 @@ import { SsrFBlockedError, type LookupFn } from "../../infra/net/ssrf.js"; import { logDebug } from "../../logger.js"; import type { RuntimeWebFetchMetadata } from "../../secrets/runtime-web-tools.types.js"; import { wrapExternalContent, wrapWebContent } from "../../security/external-content.js"; +import { normalizeOptionalString } from "../../shared/string-coerce.js"; import { isRecord } from "../../utils.js"; import { resolveWebFetchDefinition } from "../../web-fetch/runtime.js"; import { resolveWebProviderConfig } from "../../web/provider-runtime-shared.js"; @@ -247,10 +248,7 @@ type WebFetchRuntimeParams = { }; function normalizeProviderFinalUrl(value: unknown): string | undefined { - if (typeof value !== "string") { - return undefined; - } - const trimmed = value.trim(); + const trimmed = normalizeOptionalString(value); if (!trimmed) { return undefined; } diff --git a/src/cli/message-secret-scope.ts b/src/cli/message-secret-scope.ts index 5dd72655ec6..a60afb550ef 100644 --- a/src/cli/message-secret-scope.ts +++ b/src/cli/message-secret-scope.ts @@ -1,4 +1,5 @@ import { normalizeAccountId } from "../routing/session-key.js"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; import { isDeliverableMessageChannel, normalizeMessageChannel } from "../utils/message-channel.js"; function resolveScopedChannelCandidate(value: unknown): string | undefined { @@ -13,10 +14,7 @@ function resolveScopedChannelCandidate(value: unknown): string | undefined { } function resolveChannelFromTargetValue(target: unknown): string | undefined { - if (typeof target !== "string") { - return undefined; - } - const trimmed = target.trim(); + const trimmed = normalizeOptionalString(target); if (!trimmed) { return undefined; } @@ -45,10 +43,7 @@ function resolveChannelFromTargets(targets: unknown): string | undefined { } function resolveScopedAccountId(value: unknown): string | undefined { - if (typeof value !== "string") { - return undefined; - } - const trimmed = value.trim(); + const trimmed = normalizeOptionalString(value); if (!trimmed) { return undefined; } diff --git a/src/gateway/server-methods/nodes.helpers.ts b/src/gateway/server-methods/nodes.helpers.ts index 62703ee3c43..30ab7f02062 100644 --- a/src/gateway/server-methods/nodes.helpers.ts +++ b/src/gateway/server-methods/nodes.helpers.ts @@ -1,4 +1,5 @@ import type { ErrorObject } from "ajv"; +import { normalizeOptionalString } from "../../shared/string-coerce.js"; import { ErrorCodes, errorShape, formatValidationErrors } from "../protocol/index.js"; import { formatForLog } from "../ws-log.js"; import type { RespondFn } from "./types.js"; @@ -38,10 +39,7 @@ export function uniqueSortedStrings(values: unknown[]) { } export function safeParseJson(value: string | null | undefined): unknown { - if (typeof value !== "string") { - return undefined; - } - const trimmed = value.trim(); + const trimmed = normalizeOptionalString(value); if (!trimmed) { return undefined; } diff --git a/src/infra/scp-host.ts b/src/infra/scp-host.ts index 8b09163c202..527c2cb3765 100644 --- a/src/infra/scp-host.ts +++ b/src/infra/scp-host.ts @@ -1,3 +1,5 @@ +import { normalizeOptionalString } from "../shared/string-coerce.js"; + const SSH_TOKEN = /^[A-Za-z0-9._-]+$/; const BRACKETED_IPV6 = /^\[[0-9A-Fa-f:.%]+\]$/; const WHITESPACE = /\s/; @@ -14,10 +16,7 @@ function hasControlOrWhitespace(value: string): boolean { } export function normalizeScpRemoteHost(value: string | null | undefined): string | undefined { - if (typeof value !== "string") { - return undefined; - } - const trimmed = value.trim(); + const trimmed = normalizeOptionalString(value); if (!trimmed) { return undefined; } @@ -63,10 +62,7 @@ export function isSafeScpRemoteHost(value: string | null | undefined): boolean { } export function normalizeScpRemotePath(value: string | null | undefined): string | undefined { - if (typeof value !== "string") { - return undefined; - } - const trimmed = value.trim(); + const trimmed = normalizeOptionalString(value); if (!trimmed || !trimmed.startsWith("/")) { return undefined; } diff --git a/src/plugins/config-normalization-shared.ts b/src/plugins/config-normalization-shared.ts index b9708378013..b2f8ac26056 100644 --- a/src/plugins/config-normalization-shared.ts +++ b/src/plugins/config-normalization-shared.ts @@ -1,5 +1,6 @@ import { normalizeChatChannelId } from "../channels/ids.js"; import type { OpenClawConfig } from "../config/config.js"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; import { defaultSlotIdForKey } from "./slots.js"; export type NormalizedPluginsConfig = { @@ -41,10 +42,7 @@ function normalizeList(value: unknown, normalizePluginId: NormalizePluginId): st } function normalizeSlotValue(value: unknown): string | null | undefined { - if (typeof value !== "string") { - return undefined; - } - const trimmed = value.trim(); + const trimmed = normalizeOptionalString(value); if (!trimmed) { return undefined; } @@ -97,8 +95,8 @@ function normalizePluginEntries( ), allowedModels: Array.isArray((subagentRaw as { allowedModels?: unknown }).allowedModels) ? ((subagentRaw as { allowedModels?: unknown }).allowedModels as unknown[]) - .map((model) => (typeof model === "string" ? model.trim() : "")) - .filter(Boolean) + .map((model) => normalizeOptionalString(model)) + .filter((model): model is string => Boolean(model)) : undefined, } : undefined; diff --git a/src/shared/assistant-identity-values.ts b/src/shared/assistant-identity-values.ts index 8894ee129d3..f21e507c902 100644 --- a/src/shared/assistant-identity-values.ts +++ b/src/shared/assistant-identity-values.ts @@ -1,11 +1,10 @@ +import { normalizeOptionalString } from "./string-coerce.js"; + export function coerceIdentityValue( value: string | undefined, maxLength: number, ): string | undefined { - if (typeof value !== "string") { - return undefined; - } - const trimmed = value.trim(); + const trimmed = normalizeOptionalString(value); if (!trimmed) { return undefined; }