mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-10 08:20:09 +00:00
refactor: dedupe reader helpers
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { normalizeTrimmedStringList } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { normalizeOptionalTrimmedStringList } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
type BrowserConfig,
|
||||
type BrowserProfileConfig,
|
||||
@@ -113,13 +113,7 @@ function resolveCdpPortRangeStart(
|
||||
return start;
|
||||
}
|
||||
|
||||
function normalizeStringList(raw: string[] | undefined): string[] | undefined {
|
||||
if (!Array.isArray(raw) || raw.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const values = normalizeTrimmedStringList(raw);
|
||||
return values.length > 0 ? values : undefined;
|
||||
}
|
||||
const normalizeStringList = normalizeOptionalTrimmedStringList;
|
||||
|
||||
function resolveBrowserSsrFPolicy(cfg: BrowserConfig | undefined): SsrFPolicy | undefined {
|
||||
const rawPolicy = cfg?.ssrfPolicy as
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import type * as Lark from "@larksuiteoapi/node-sdk";
|
||||
import { readStringValue } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { cleanBlocksForDescendant } from "./docx-table-ops.js";
|
||||
import type { FeishuDocxBlock, FeishuDocxBlockChild } from "./docx-types.js";
|
||||
|
||||
@@ -25,7 +26,8 @@ function normalizeChildIds(children: string[] | string | undefined): string[] |
|
||||
if (Array.isArray(children)) {
|
||||
return children;
|
||||
}
|
||||
return typeof children === "string" ? [children] : undefined;
|
||||
const child = readStringValue(children);
|
||||
return child ? [child] : undefined;
|
||||
}
|
||||
|
||||
function toDescendantBlock(block: FeishuDocxBlock): DocxDescendantCreateBlock {
|
||||
|
||||
@@ -25,10 +25,6 @@ export const EXTERNAL_CODE_PLUGIN_REQUIRED_FIELD_PATHS = [
|
||||
"openclaw.build.openclawVersion",
|
||||
] as const;
|
||||
|
||||
function getTrimmedString(value: unknown): string | undefined {
|
||||
return normalizeOptionalString(value);
|
||||
}
|
||||
|
||||
function readOpenClawBlock(packageJson: unknown) {
|
||||
const root = isRecord(packageJson) ? packageJson : undefined;
|
||||
const openclaw = isRecord(root?.openclaw) ? root.openclaw : undefined;
|
||||
@@ -42,26 +38,26 @@ export function normalizeExternalPluginCompatibility(
|
||||
packageJson: unknown,
|
||||
): ExternalPluginCompatibility | undefined {
|
||||
const { root, compat, build, install } = readOpenClawBlock(packageJson);
|
||||
const version = getTrimmedString(root?.version);
|
||||
const minHostVersion = getTrimmedString(install?.minHostVersion);
|
||||
const version = normalizeOptionalString(root?.version);
|
||||
const minHostVersion = normalizeOptionalString(install?.minHostVersion);
|
||||
const compatibility: ExternalPluginCompatibility = {};
|
||||
|
||||
const pluginApi = getTrimmedString(compat?.pluginApi);
|
||||
const pluginApi = normalizeOptionalString(compat?.pluginApi);
|
||||
if (pluginApi) {
|
||||
compatibility.pluginApiRange = pluginApi;
|
||||
}
|
||||
|
||||
const minGatewayVersion = getTrimmedString(compat?.minGatewayVersion) ?? minHostVersion;
|
||||
const minGatewayVersion = normalizeOptionalString(compat?.minGatewayVersion) ?? minHostVersion;
|
||||
if (minGatewayVersion) {
|
||||
compatibility.minGatewayVersion = minGatewayVersion;
|
||||
}
|
||||
|
||||
const builtWithOpenClawVersion = getTrimmedString(build?.openclawVersion) ?? version;
|
||||
const builtWithOpenClawVersion = normalizeOptionalString(build?.openclawVersion) ?? version;
|
||||
if (builtWithOpenClawVersion) {
|
||||
compatibility.builtWithOpenClawVersion = builtWithOpenClawVersion;
|
||||
}
|
||||
|
||||
const pluginSdkVersion = getTrimmedString(build?.pluginSdkVersion);
|
||||
const pluginSdkVersion = normalizeOptionalString(build?.pluginSdkVersion);
|
||||
if (pluginSdkVersion) {
|
||||
compatibility.pluginSdkVersion = pluginSdkVersion;
|
||||
}
|
||||
@@ -72,10 +68,10 @@ export function normalizeExternalPluginCompatibility(
|
||||
export function listMissingExternalCodePluginFieldPaths(packageJson: unknown): string[] {
|
||||
const { compat, build } = readOpenClawBlock(packageJson);
|
||||
const missing: string[] = [];
|
||||
if (!getTrimmedString(compat?.pluginApi)) {
|
||||
if (!normalizeOptionalString(compat?.pluginApi)) {
|
||||
missing.push("openclaw.compat.pluginApi");
|
||||
}
|
||||
if (!getTrimmedString(build?.openclawVersion)) {
|
||||
if (!normalizeOptionalString(build?.openclawVersion)) {
|
||||
missing.push("openclaw.build.openclawVersion");
|
||||
}
|
||||
return missing;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { z } from "zod";
|
||||
import { PROTOCOL_VERSION } from "../../src/gateway/protocol/index.ts";
|
||||
import { formatErrorMessage } from "../../src/infra/errors.ts";
|
||||
import { rawDataToString } from "../../src/infra/ws.ts";
|
||||
import { readStringValue } from "../../src/shared/string-coerce.ts";
|
||||
|
||||
export const ClaudeChannelNotificationSchema = z.object({
|
||||
method: z.literal("notifications/claude/channel"),
|
||||
@@ -66,8 +67,7 @@ export function extractTextFromGatewayPayload(
|
||||
if (!first || typeof first !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
const text = (first as { text?: unknown }).text;
|
||||
return typeof text === "string" ? text : undefined;
|
||||
return readStringValue((first as { text?: unknown }).text);
|
||||
}
|
||||
|
||||
export async function waitFor<T>(
|
||||
|
||||
@@ -2,6 +2,7 @@ import { getChannelPlugin, normalizeChannelId } from "../channels/plugins/index.
|
||||
import { normalizeTargetForProvider } from "../infra/outbound/target-normalization.js";
|
||||
import { splitMediaFromOutput } from "../media/parse.js";
|
||||
import { pluginRegistrationContractRegistry } from "../plugins/contracts/registry.js";
|
||||
import { normalizeOptionalString, readStringValue } from "../shared/string-coerce.js";
|
||||
import { truncateUtf16Safe } from "../utils.js";
|
||||
import { collectTextContentBlocks } from "./content-blocks.js";
|
||||
import { type MessagingToolSend } from "./pi-embedded-messaging.js";
|
||||
@@ -98,12 +99,12 @@ export function sanitizeToolResult(result: unknown): unknown {
|
||||
return item;
|
||||
}
|
||||
const entry = item as Record<string, unknown>;
|
||||
const type = typeof entry.type === "string" ? entry.type : undefined;
|
||||
const type = readStringValue(entry.type);
|
||||
if (type === "text" && typeof entry.text === "string") {
|
||||
return { ...entry, text: truncateToolText(entry.text) };
|
||||
}
|
||||
if (type === "image") {
|
||||
const data = typeof entry.data === "string" ? entry.data : undefined;
|
||||
const data = readStringValue(entry.data);
|
||||
const bytes = data ? data.length : undefined;
|
||||
const cleaned = { ...entry };
|
||||
delete cleaned.data;
|
||||
@@ -181,7 +182,7 @@ function readToolResultDetails(result: unknown): Record<string, unknown> | undef
|
||||
|
||||
function readToolResultStatus(result: unknown): string | undefined {
|
||||
const status = readToolResultDetails(result)?.status;
|
||||
return typeof status === "string" ? status.trim().toLowerCase() : undefined;
|
||||
return normalizeOptionalString(status)?.toLowerCase();
|
||||
}
|
||||
|
||||
function isExternalToolResult(result: unknown): boolean {
|
||||
@@ -372,11 +373,11 @@ export function extractToolErrorMessage(result: unknown): string | undefined {
|
||||
}
|
||||
|
||||
function resolveMessageToolTarget(args: Record<string, unknown>): string | undefined {
|
||||
const toRaw = typeof args.to === "string" ? args.to : undefined;
|
||||
const toRaw = readStringValue(args.to);
|
||||
if (toRaw) {
|
||||
return toRaw;
|
||||
}
|
||||
return typeof args.target === "string" ? args.target : undefined;
|
||||
return readStringValue(args.target);
|
||||
}
|
||||
|
||||
export function extractMessagingToolSend(
|
||||
@@ -385,8 +386,7 @@ export function extractMessagingToolSend(
|
||||
): MessagingToolSend | undefined {
|
||||
// Provider docking: new provider tools must implement plugin.actions.extractToolSend.
|
||||
const action = typeof args.action === "string" ? args.action.trim() : "";
|
||||
const accountIdRaw = typeof args.accountId === "string" ? args.accountId.trim() : undefined;
|
||||
const accountId = accountIdRaw ? accountIdRaw : undefined;
|
||||
const accountId = normalizeOptionalString(args.accountId);
|
||||
if (toolName === "message") {
|
||||
if (action !== "send" && action !== "thread-reply") {
|
||||
return undefined;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { normalizeChannelId as normalizePluginChannelId } from "../../channels/p
|
||||
import type { ChannelThreadingAdapter } from "../../channels/plugins/types.core.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { ReplyToMode } from "../../config/types.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import type { OriginatingChannelType } from "../templating.js";
|
||||
import type { ReplyPayload, ReplyThreadingPolicy } from "../types.js";
|
||||
import { isSingleUseReplyToMode } from "./reply-reference.js";
|
||||
@@ -27,7 +28,7 @@ function resolveReplyToModeChannelKey(channel?: OriginatingChannelType): string
|
||||
if (normalized) {
|
||||
return normalized;
|
||||
}
|
||||
return typeof channel === "string" ? channel.trim().toLowerCase() || undefined : undefined;
|
||||
return normalizeOptionalString(channel)?.toLowerCase();
|
||||
}
|
||||
|
||||
export function resolveConfiguredReplyToMode(
|
||||
@@ -159,7 +160,7 @@ export function createReplyToModeFilterForChannel(
|
||||
mode: ReplyToMode,
|
||||
channel?: OriginatingChannelType,
|
||||
) {
|
||||
const normalized = typeof channel === "string" ? channel.trim().toLowerCase() : undefined;
|
||||
const normalized = normalizeOptionalString(channel)?.toLowerCase();
|
||||
const isWebchat = normalized === "webchat";
|
||||
// Default: allow explicit reply tags/directives even when replyToMode is "off".
|
||||
// Unknown channels fail closed; internal webchat stays allowed.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { stripInboundMetadata } from "../auto-reply/reply/strip-inbound-meta.js";
|
||||
import { normalizeOptionalString, readStringValue } from "../shared/string-coerce.js";
|
||||
|
||||
const DEDUPE_TIMESTAMP_WINDOW_MS = 5 * 60 * 1000;
|
||||
|
||||
@@ -7,17 +8,22 @@ function extractComparableText(message: unknown): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
const record = message as { role?: unknown; text?: unknown; content?: unknown };
|
||||
const role = typeof record.role === "string" ? record.role : undefined;
|
||||
const role = readStringValue(record.role);
|
||||
const parts: string[] = [];
|
||||
if (typeof record.text === "string") {
|
||||
parts.push(record.text);
|
||||
const text = readStringValue(record.text);
|
||||
if (text !== undefined) {
|
||||
parts.push(text);
|
||||
}
|
||||
if (typeof record.content === "string") {
|
||||
parts.push(record.content);
|
||||
const content = readStringValue(record.content);
|
||||
if (content !== undefined) {
|
||||
parts.push(content);
|
||||
} else if (Array.isArray(record.content)) {
|
||||
for (const block of record.content) {
|
||||
if (block && typeof block === "object" && "text" in block && typeof block.text === "string") {
|
||||
parts.push(block.text);
|
||||
if (block && typeof block === "object" && "text" in block) {
|
||||
const blockText = readStringValue(block.text);
|
||||
if (blockText !== undefined) {
|
||||
parts.push(blockText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,8 +54,7 @@ function resolveComparableRole(message: unknown): string | undefined {
|
||||
if (!message || typeof message !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
const role = (message as { role?: unknown }).role;
|
||||
return typeof role === "string" ? role : undefined;
|
||||
return readStringValue((message as { role?: unknown }).role);
|
||||
}
|
||||
|
||||
function resolveImportedExternalId(message: unknown): string | undefined {
|
||||
@@ -62,8 +67,7 @@ function resolveImportedExternalId(message: unknown): string | undefined {
|
||||
typeof (message as { __openclaw?: unknown }).__openclaw === "object"
|
||||
? ((message as { __openclaw?: Record<string, unknown> }).__openclaw ?? {})
|
||||
: undefined;
|
||||
const externalId = meta?.externalId;
|
||||
return typeof externalId === "string" && externalId.trim() ? externalId : undefined;
|
||||
return normalizeOptionalString(meta?.externalId);
|
||||
}
|
||||
|
||||
function isEquivalentImportedMessage(existing: unknown, imported: unknown): boolean {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { resolveMissingRequestedScope } from "../shared/operator-scope-compat.js";
|
||||
import { normalizeTrimmedStringList } from "../shared/string-normalization.js";
|
||||
import { normalizeArrayBackedTrimmedStringList } from "../shared/string-normalization.js";
|
||||
import { type NodeApprovalScope, resolveNodePairApprovalScopes } from "./node-pairing-authz.js";
|
||||
import {
|
||||
createAsyncLock,
|
||||
@@ -67,14 +67,6 @@ const OPERATOR_ROLE = "operator";
|
||||
|
||||
const withLock = createAsyncLock();
|
||||
|
||||
function normalizeStringList(values?: string[]): string[] | undefined {
|
||||
if (!Array.isArray(values)) {
|
||||
return undefined;
|
||||
}
|
||||
const normalized = normalizeTrimmedStringList(values);
|
||||
return normalized.length > 0 ? normalized : [];
|
||||
}
|
||||
|
||||
function buildPendingNodePairingRequest(params: {
|
||||
requestId?: string;
|
||||
req: NodePairingRequestInput;
|
||||
@@ -89,8 +81,8 @@ function buildPendingNodePairingRequest(params: {
|
||||
uiVersion: params.req.uiVersion,
|
||||
deviceFamily: params.req.deviceFamily,
|
||||
modelIdentifier: params.req.modelIdentifier,
|
||||
caps: normalizeStringList(params.req.caps),
|
||||
commands: normalizeStringList(params.req.commands),
|
||||
caps: normalizeArrayBackedTrimmedStringList(params.req.caps),
|
||||
commands: normalizeArrayBackedTrimmedStringList(params.req.commands),
|
||||
permissions: params.req.permissions,
|
||||
remoteIp: params.req.remoteIp,
|
||||
silent: params.req.silent,
|
||||
@@ -111,8 +103,8 @@ function refreshPendingNodePairingRequest(
|
||||
uiVersion: incoming.uiVersion ?? existing.uiVersion,
|
||||
deviceFamily: incoming.deviceFamily ?? existing.deviceFamily,
|
||||
modelIdentifier: incoming.modelIdentifier ?? existing.modelIdentifier,
|
||||
caps: normalizeStringList(incoming.caps) ?? existing.caps,
|
||||
commands: normalizeStringList(incoming.commands) ?? existing.commands,
|
||||
caps: normalizeArrayBackedTrimmedStringList(incoming.caps) ?? existing.caps,
|
||||
commands: normalizeArrayBackedTrimmedStringList(incoming.commands) ?? existing.commands,
|
||||
permissions: incoming.permissions ?? existing.permissions,
|
||||
remoteIp: incoming.remoteIp ?? existing.remoteIp,
|
||||
// Preserve interactive visibility if either request needs attention.
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { readStringValue } from "./string-coerce.js";
|
||||
|
||||
export function extractFirstTextBlock(message: unknown): string | undefined {
|
||||
if (!message || typeof message !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
const content = (message as { content?: unknown }).content;
|
||||
if (typeof content === "string") {
|
||||
return content;
|
||||
const inline = readStringValue(content);
|
||||
if (inline !== undefined) {
|
||||
return inline;
|
||||
}
|
||||
if (!Array.isArray(content) || content.length === 0) {
|
||||
return undefined;
|
||||
@@ -13,8 +16,7 @@ export function extractFirstTextBlock(message: unknown): string | undefined {
|
||||
if (!first || typeof first !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
const text = (first as { text?: unknown }).text;
|
||||
return typeof text === "string" ? text : undefined;
|
||||
return readStringValue((first as { text?: unknown }).text);
|
||||
}
|
||||
|
||||
export type AssistantPhase = "commentary" | "final_answer";
|
||||
|
||||
@@ -15,6 +15,18 @@ export function normalizeTrimmedStringList(value: unknown): string[] {
|
||||
);
|
||||
}
|
||||
|
||||
export function normalizeOptionalTrimmedStringList(value: unknown): string[] | undefined {
|
||||
const normalized = normalizeTrimmedStringList(value);
|
||||
return normalized.length > 0 ? normalized : undefined;
|
||||
}
|
||||
|
||||
export function normalizeArrayBackedTrimmedStringList(value: unknown): string[] | undefined {
|
||||
if (!Array.isArray(value)) {
|
||||
return undefined;
|
||||
}
|
||||
return normalizeTrimmedStringList(value);
|
||||
}
|
||||
|
||||
export function normalizeSingleOrTrimmedStringList(value: unknown): string[] {
|
||||
if (Array.isArray(value)) {
|
||||
return normalizeTrimmedStringList(value);
|
||||
|
||||
Reference in New Issue
Block a user