refactor: dedupe helper alias readers

This commit is contained in:
Peter Steinberger
2026-04-07 08:09:53 +01:00
parent 424b65b697
commit dbc67a5626
13 changed files with 73 additions and 118 deletions

View File

@@ -1,3 +1,5 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
const REPLY_CACHE_MAX = 2000;
const REPLY_CACHE_TTL_MS = 6 * 60 * 60 * 1000;
@@ -21,11 +23,6 @@ const blueBubblesShortIdToUuid = new Map<string, string>();
const blueBubblesUuidToShortId = new Map<string, string>();
let blueBubblesShortIdCounter = 0;
function trimOrUndefined(value?: string | null): string | undefined {
const trimmed = value?.trim();
return trimmed ? trimmed : undefined;
}
function generateShortId(): string {
blueBubblesShortIdCounter += 1;
return String(blueBubblesShortIdCounter);
@@ -158,10 +155,10 @@ export function resolveReplyContextFromCache(params: {
return null;
}
const chatGuid = trimOrUndefined(params.chatGuid);
const chatIdentifier = trimOrUndefined(params.chatIdentifier);
const cachedChatGuid = trimOrUndefined(cached.chatGuid);
const cachedChatIdentifier = trimOrUndefined(cached.chatIdentifier);
const chatGuid = normalizeOptionalString(params.chatGuid);
const chatIdentifier = normalizeOptionalString(params.chatIdentifier);
const cachedChatGuid = normalizeOptionalString(cached.chatGuid);
const cachedChatIdentifier = normalizeOptionalString(cached.chatIdentifier);
const chatId = typeof params.chatId === "number" ? params.chatId : undefined;
const cachedChatId = typeof cached.chatId === "number" ? cached.chatId : undefined;

View File

@@ -1,4 +1,5 @@
import { createHash } from "node:crypto";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { extractHandleFromChatGuid, normalizeBlueBubblesHandle } from "./targets.js";
type SelfChatCacheKeyParts = {
@@ -39,11 +40,6 @@ function digestText(text: string): string {
return createHash("sha256").update(text).digest("base64url");
}
function trimOrUndefined(value?: string | null): string | undefined {
const trimmed = value?.trim();
return trimmed ? trimmed : undefined;
}
function resolveCanonicalChatTarget(parts: SelfChatCacheKeyParts): string | null {
const handleFromGuid = parts.chatGuid ? extractHandleFromChatGuid(parts.chatGuid) : null;
if (handleFromGuid) {
@@ -56,8 +52,8 @@ function resolveCanonicalChatTarget(parts: SelfChatCacheKeyParts): string | null
}
return (
trimOrUndefined(parts.chatGuid) ??
trimOrUndefined(parts.chatIdentifier) ??
normalizeOptionalString(parts.chatGuid) ??
normalizeOptionalString(parts.chatIdentifier) ??
(typeof parts.chatId === "number" ? String(parts.chatId) : null)
);
}

View File

@@ -2,18 +2,15 @@ import type { OpenClawConfig } from "../../config/config.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import type { AuthProfileStore } from "./types.js";
function trimOptionalString(value: string | null | undefined): string | undefined {
return normalizeOptionalString(value);
}
function resolveStoredMetadata(store: AuthProfileStore | undefined, profileId: string) {
const profile = store?.profiles[profileId];
if (!profile) {
return {};
}
return {
displayName: "displayName" in profile ? trimOptionalString(profile.displayName) : undefined,
email: "email" in profile ? trimOptionalString(profile.email) : undefined,
displayName:
"displayName" in profile ? normalizeOptionalString(profile.displayName) : undefined,
email: "email" in profile ? normalizeOptionalString(profile.email) : undefined,
};
}
@@ -22,8 +19,8 @@ export function buildAuthProfileId(params: {
profileName?: string | null;
profilePrefix?: string;
}): string {
const profilePrefix = trimOptionalString(params.profilePrefix) ?? params.providerId;
const profileName = trimOptionalString(params.profileName) ?? "default";
const profilePrefix = normalizeOptionalString(params.profilePrefix) ?? params.providerId;
const profileName = normalizeOptionalString(params.profileName) ?? "default";
return `${profilePrefix}:${profileName}`;
}
@@ -35,7 +32,7 @@ export function resolveAuthProfileMetadata(params: {
const configured = params.cfg?.auth?.profiles?.[params.profileId];
const stored = resolveStoredMetadata(params.store, params.profileId);
return {
displayName: trimOptionalString(configured?.displayName) ?? stored.displayName,
email: trimOptionalString(configured?.email) ?? stored.email,
displayName: normalizeOptionalString(configured?.displayName) ?? stored.displayName,
email: normalizeOptionalString(configured?.email) ?? stored.email,
};
}

View File

@@ -5,12 +5,8 @@ import { normalizeProviderId } from "./model-selection.js";
const CLAUDE_CLI_BACKEND_ID = "claude-cli";
function trimOptional(value: string | undefined): string | undefined {
return normalizeOptionalString(value);
}
export function hashCliSessionText(value: string | undefined): string | undefined {
const trimmed = trimOptional(value);
const trimmed = normalizeOptionalString(value);
if (!trimmed) {
return undefined;
}
@@ -26,14 +22,14 @@ export function getCliSessionBinding(
}
const normalized = normalizeProviderId(provider);
const fromBindings = entry.cliSessionBindings?.[normalized];
const bindingSessionId = trimOptional(fromBindings?.sessionId);
const bindingSessionId = normalizeOptionalString(fromBindings?.sessionId);
if (bindingSessionId) {
return {
sessionId: bindingSessionId,
authProfileId: trimOptional(fromBindings?.authProfileId),
authEpoch: trimOptional(fromBindings?.authEpoch),
extraSystemPromptHash: trimOptional(fromBindings?.extraSystemPromptHash),
mcpConfigHash: trimOptional(fromBindings?.mcpConfigHash),
authProfileId: normalizeOptionalString(fromBindings?.authProfileId),
authEpoch: normalizeOptionalString(fromBindings?.authEpoch),
extraSystemPromptHash: normalizeOptionalString(fromBindings?.extraSystemPromptHash),
mcpConfigHash: normalizeOptionalString(fromBindings?.mcpConfigHash),
};
}
const fromMap = entry.cliSessionIds?.[normalized];
@@ -75,15 +71,17 @@ export function setCliSessionBinding(
...entry.cliSessionBindings,
[normalized]: {
sessionId: trimmed,
...(trimOptional(binding.authProfileId)
? { authProfileId: trimOptional(binding.authProfileId) }
...(normalizeOptionalString(binding.authProfileId)
? { authProfileId: normalizeOptionalString(binding.authProfileId) }
: {}),
...(trimOptional(binding.authEpoch) ? { authEpoch: trimOptional(binding.authEpoch) } : {}),
...(trimOptional(binding.extraSystemPromptHash)
? { extraSystemPromptHash: trimOptional(binding.extraSystemPromptHash) }
...(normalizeOptionalString(binding.authEpoch)
? { authEpoch: normalizeOptionalString(binding.authEpoch) }
: {}),
...(trimOptional(binding.mcpConfigHash)
? { mcpConfigHash: trimOptional(binding.mcpConfigHash) }
...(normalizeOptionalString(binding.extraSystemPromptHash)
? { extraSystemPromptHash: normalizeOptionalString(binding.extraSystemPromptHash) }
: {}),
...(normalizeOptionalString(binding.mcpConfigHash)
? { mcpConfigHash: normalizeOptionalString(binding.mcpConfigHash) }
: {}),
},
};
@@ -127,27 +125,27 @@ export function resolveCliSessionReuse(params: {
invalidatedReason?: "auth-profile" | "auth-epoch" | "system-prompt" | "mcp";
} {
const binding = params.binding;
const sessionId = trimOptional(binding?.sessionId);
const sessionId = normalizeOptionalString(binding?.sessionId);
if (!sessionId) {
return {};
}
const currentAuthProfileId = trimOptional(params.authProfileId);
const currentAuthEpoch = trimOptional(params.authEpoch);
const currentExtraSystemPromptHash = trimOptional(params.extraSystemPromptHash);
const currentMcpConfigHash = trimOptional(params.mcpConfigHash);
const storedAuthProfileId = trimOptional(binding?.authProfileId);
const currentAuthProfileId = normalizeOptionalString(params.authProfileId);
const currentAuthEpoch = normalizeOptionalString(params.authEpoch);
const currentExtraSystemPromptHash = normalizeOptionalString(params.extraSystemPromptHash);
const currentMcpConfigHash = normalizeOptionalString(params.mcpConfigHash);
const storedAuthProfileId = normalizeOptionalString(binding?.authProfileId);
if (storedAuthProfileId !== currentAuthProfileId) {
return { invalidatedReason: "auth-profile" };
}
const storedAuthEpoch = trimOptional(binding?.authEpoch);
const storedAuthEpoch = normalizeOptionalString(binding?.authEpoch);
if (storedAuthEpoch !== currentAuthEpoch) {
return { invalidatedReason: "auth-epoch" };
}
const storedExtraSystemPromptHash = trimOptional(binding?.extraSystemPromptHash);
const storedExtraSystemPromptHash = normalizeOptionalString(binding?.extraSystemPromptHash);
if (storedExtraSystemPromptHash !== currentExtraSystemPromptHash) {
return { invalidatedReason: "system-prompt" };
}
const storedMcpConfigHash = trimOptional(binding?.mcpConfigHash);
const storedMcpConfigHash = normalizeOptionalString(binding?.mcpConfigHash);
if (storedMcpConfigHash !== currentMcpConfigHash) {
return { invalidatedReason: "mcp" };
}

View File

@@ -12,10 +12,6 @@ export type OwnerDisplaySecretResolution = {
generatedSecret?: string;
};
function trimToUndefined(value?: string): string | undefined {
return normalizeOptionalString(value);
}
/**
* Resolve owner display settings for prompt rendering.
* Keep auth secrets decoupled from owner hash secrets.
@@ -27,7 +23,7 @@ export function resolveOwnerDisplaySetting(config?: OpenClawConfig): OwnerDispla
}
return {
ownerDisplay: "hash",
ownerDisplaySecret: trimToUndefined(config?.commands?.ownerDisplaySecret),
ownerDisplaySecret: normalizeOptionalString(config?.commands?.ownerDisplaySecret),
};
}

View File

@@ -1,5 +1,3 @@
import { normalizeOptionalString } from "../shared/string-coerce.js";
export function normalizeSubagentSessionKey(value: unknown): string | undefined {
return normalizeOptionalString(value);
}
export const normalizeSubagentSessionKey = normalizeOptionalString;

View File

@@ -15,10 +15,6 @@ let sessionsResolutionDeps: {
callGateway: GatewayCaller;
} = defaultSessionsResolutionDeps;
function normalizeKey(value?: string) {
return normalizeOptionalString(value);
}
export function resolveMainSessionAlias(cfg: OpenClawConfig) {
const mainKey = normalizeMainKey(cfg.session?.mainKey);
const scope = cfg.session?.scope ?? "per-sender";
@@ -149,7 +145,7 @@ export async function isResolvedSessionVisibleToRequester(params: {
export { looksLikeSessionId };
export function looksLikeSessionKey(value: string): boolean {
const raw = value.trim();
const raw = normalizeOptionalString(value) ?? "";
if (!raw) {
return false;
}
@@ -460,7 +456,7 @@ export async function resolveVisibleSessionReference(params: {
}
export function normalizeOptionalKey(value?: string) {
return normalizeKey(value);
return normalizeOptionalString(value);
}
export const __testing = {

View File

@@ -16,10 +16,6 @@ function hasAnthropicVertexMetadataServerAdc(env: NodeJS.ProcessEnv = process.en
return explicitMetadataOptIn === "1" || explicitMetadataOptIn?.toLowerCase() === "true";
}
function normalizeOptionalPathInput(value: unknown): string | undefined {
return normalizeOptionalString(value);
}
function resolveAnthropicVertexDefaultAdcPath(env: NodeJS.ProcessEnv = process.env): string {
return platform() === "win32"
? join(
@@ -33,7 +29,7 @@ function resolveAnthropicVertexDefaultAdcPath(env: NodeJS.ProcessEnv = process.e
function resolveAnthropicVertexAdcCredentialsPathCandidate(
env: NodeJS.ProcessEnv = process.env,
): string | undefined {
const explicit = normalizeOptionalPathInput(env.GOOGLE_APPLICATION_CREDENTIALS);
const explicit = normalizeOptionalString(env.GOOGLE_APPLICATION_CREDENTIALS);
if (explicit) {
return explicit;
}

View File

@@ -3,16 +3,12 @@ import type { OpenClawConfig } from "./config-runtime.js";
type ApprovalKind = "exec" | "plugin";
function defaultNormalizeSenderId(value: string): string | undefined {
return normalizeOptionalString(value);
}
export function createResolvedApproverActionAuthAdapter(params: {
channelLabel: string;
resolveApprovers: (params: { cfg: OpenClawConfig; accountId?: string | null }) => string[];
normalizeSenderId?: (value: string) => string | undefined;
}) {
const normalizeSenderId = params.normalizeSenderId ?? defaultNormalizeSenderId;
const normalizeSenderId = params.normalizeSenderId ?? normalizeOptionalString;
return {
authorizeActorAction({

View File

@@ -24,10 +24,6 @@ type ApprovalProfileParams = {
accountId?: string | null;
};
function defaultNormalizeSenderId(value: string): string | undefined {
return normalizeOptionalString(value);
}
function isApprovalTargetsMode(cfg: OpenClawConfig): boolean {
const execApprovals = cfg.approvals?.exec;
if (!execApprovals?.enabled) {
@@ -60,7 +56,7 @@ export function isChannelExecApprovalTargetRecipient(params: {
normalizedAccountId?: string;
}) => boolean;
}): boolean {
const normalizeSenderId = params.normalizeSenderId ?? defaultNormalizeSenderId;
const normalizeSenderId = params.normalizeSenderId ?? normalizeOptionalString;
const normalizedSenderId = params.senderId ? normalizeSenderId(params.senderId) : undefined;
const normalizedChannel = params.channel.trim().toLowerCase();
if (!normalizedSenderId || !isApprovalTargetsMode(params.cfg)) {
@@ -100,7 +96,7 @@ export function createChannelExecApprovalProfile(params: {
fallbackAgentIdFromSessionKey?: boolean;
requireClientEnabledForLocalPromptSuppression?: boolean;
}) {
const normalizeSenderId = params.normalizeSenderId ?? defaultNormalizeSenderId;
const normalizeSenderId = params.normalizeSenderId ?? normalizeOptionalString;
const isClientEnabled = (input: ApprovalProfileParams): boolean => {
const config = params.resolveConfig(input);

View File

@@ -6,12 +6,8 @@ import {
} from "./task-flow-registry.js";
import type { TaskFlowRecord } from "./task-flow-registry.types.js";
function normalizeOwnerKey(ownerKey?: string): string | undefined {
return normalizeOptionalString(ownerKey);
}
function canOwnerAccessFlow(flow: TaskFlowRecord, callerOwnerKey: string): boolean {
return normalizeOwnerKey(flow.ownerKey) === normalizeOwnerKey(callerOwnerKey);
return normalizeOptionalString(flow.ownerKey) === normalizeOptionalString(callerOwnerKey);
}
export function getTaskFlowByIdForOwner(params: {
@@ -23,14 +19,14 @@ export function getTaskFlowByIdForOwner(params: {
}
export function listTaskFlowsForOwner(params: { callerOwnerKey: string }): TaskFlowRecord[] {
const ownerKey = normalizeOwnerKey(params.callerOwnerKey);
const ownerKey = normalizeOptionalString(params.callerOwnerKey);
return ownerKey ? listTaskFlowsForOwnerKey(ownerKey) : [];
}
export function findLatestTaskFlowForOwner(params: {
callerOwnerKey: string;
}): TaskFlowRecord | undefined {
const ownerKey = normalizeOwnerKey(params.callerOwnerKey);
const ownerKey = normalizeOptionalString(params.callerOwnerKey);
return ownerKey ? findLatestTaskFlowForOwnerKey(ownerKey) : undefined;
}
@@ -45,8 +41,8 @@ export function resolveTaskFlowForLookupTokenForOwner(params: {
if (direct) {
return direct;
}
const normalizedToken = normalizeOwnerKey(params.token);
const normalizedCallerOwnerKey = normalizeOwnerKey(params.callerOwnerKey);
const normalizedToken = normalizeOptionalString(params.token);
const normalizedCallerOwnerKey = normalizeOptionalString(params.callerOwnerKey);
if (!normalizedToken || normalizedToken !== normalizedCallerOwnerKey) {
return undefined;
}

View File

@@ -93,7 +93,7 @@ function normalizeRestoredFlowRecord(record: TaskFlowRecord): TaskFlowRecord {
const syncMode = record.syncMode === "task_mirrored" ? "task_mirrored" : "managed";
const controllerId =
syncMode === "managed"
? (normalizeText(record.controllerId) ?? "core/legacy-restored")
? (normalizeOptionalString(record.controllerId) ?? "core/legacy-restored")
: undefined;
return {
...record,
@@ -103,9 +103,9 @@ function normalizeRestoredFlowRecord(record: TaskFlowRecord): TaskFlowRecord {
? { requesterOrigin: cloneStructuredValue(record.requesterOrigin)! }
: {}),
...(controllerId ? { controllerId } : {}),
currentStep: normalizeText(record.currentStep),
blockedTaskId: normalizeText(record.blockedTaskId),
blockedSummary: normalizeText(record.blockedSummary),
currentStep: normalizeOptionalString(record.currentStep),
blockedTaskId: normalizeOptionalString(record.blockedTaskId),
blockedSummary: normalizeOptionalString(record.blockedSummary),
...(record.stateJson !== undefined
? { stateJson: cloneStructuredValue(record.stateJson)! }
: {}),
@@ -136,20 +136,12 @@ function ensureNotifyPolicy(notifyPolicy?: TaskNotifyPolicy): TaskNotifyPolicy {
return notifyPolicy ?? "done_only";
}
function normalizeOwnerKey(ownerKey?: string): string | undefined {
return normalizeOptionalString(ownerKey);
}
function normalizeText(value?: string | null): string | undefined {
return normalizeOptionalString(value);
}
function normalizeJsonBlob(value: JsonValue | null | undefined): JsonValue | undefined {
return value === undefined ? undefined : cloneStructuredValue(value);
}
function assertFlowOwnerKey(ownerKey: string): string {
const normalized = normalizeOwnerKey(ownerKey);
const normalized = normalizeOptionalString(ownerKey);
if (!normalized) {
throw new Error("Flow ownerKey is required.");
}
@@ -157,7 +149,7 @@ function assertFlowOwnerKey(ownerKey: string): string {
}
function assertControllerId(controllerId?: string | null): string {
const normalized = normalizeText(controllerId);
const normalized = normalizeOptionalString(controllerId);
if (!normalized) {
throw new Error("Managed flow controllerId is required.");
}
@@ -287,9 +279,9 @@ function buildFlowRecord(params: {
status: params.status ?? "queued",
notifyPolicy: ensureNotifyPolicy(params.notifyPolicy),
goal: params.goal,
currentStep: normalizeText(params.currentStep),
blockedTaskId: normalizeText(params.blockedTaskId),
blockedSummary: normalizeText(params.blockedSummary),
currentStep: normalizeOptionalString(params.currentStep),
blockedTaskId: normalizeOptionalString(params.blockedTaskId),
blockedSummary: normalizeOptionalString(params.blockedSummary),
...(normalizeJsonBlob(params.stateJson) !== undefined
? { stateJson: normalizeJsonBlob(params.stateJson)! }
: {}),
@@ -305,7 +297,9 @@ function buildFlowRecord(params: {
function applyFlowPatch(current: TaskFlowRecord, patch: FlowRecordPatch): TaskFlowRecord {
const controllerId =
patch.controllerId === undefined ? current.controllerId : normalizeText(patch.controllerId);
patch.controllerId === undefined
? current.controllerId
: normalizeOptionalString(patch.controllerId);
if (current.syncMode === "managed") {
assertControllerId(controllerId);
}
@@ -316,15 +310,17 @@ function applyFlowPatch(current: TaskFlowRecord, patch: FlowRecordPatch): TaskFl
...(patch.goal ? { goal: patch.goal } : {}),
controllerId,
currentStep:
patch.currentStep === undefined ? current.currentStep : normalizeText(patch.currentStep),
patch.currentStep === undefined
? current.currentStep
: normalizeOptionalString(patch.currentStep),
blockedTaskId:
patch.blockedTaskId === undefined
? current.blockedTaskId
: normalizeText(patch.blockedTaskId),
: normalizeOptionalString(patch.blockedTaskId),
blockedSummary:
patch.blockedSummary === undefined
? current.blockedSummary
: normalizeText(patch.blockedSummary),
: normalizeOptionalString(patch.blockedSummary),
stateJson:
patch.stateJson === undefined ? current.stateJson : normalizeJsonBlob(patch.stateJson),
waitJson: patch.waitJson === undefined ? current.waitJson : normalizeJsonBlob(patch.waitJson),
@@ -494,7 +490,8 @@ export function setFlowWaiting(params: {
expectedRevision: params.expectedRevision,
patch: {
status:
normalizeText(params.blockedTaskId) || normalizeText(params.blockedSummary)
normalizeOptionalString(params.blockedTaskId) ||
normalizeOptionalString(params.blockedSummary)
? "blocked"
: "waiting",
currentStep: params.currentStep,

View File

@@ -8,14 +8,10 @@ import {
import type { TaskRecord } from "./task-registry.types.js";
import { buildTaskStatusSnapshot } from "./task-status.js";
function normalizeOwnerKey(ownerKey?: string): string | undefined {
return normalizeOptionalString(ownerKey);
}
function canOwnerAccessTask(task: TaskRecord, callerOwnerKey: string): boolean {
return (
task.scopeKind === "session" &&
normalizeOwnerKey(task.ownerKey) === normalizeOwnerKey(callerOwnerKey)
normalizeOptionalString(task.ownerKey) === normalizeOptionalString(callerOwnerKey)
);
}