UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

260 lines (259 loc) 11.2 kB
import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js"; import "./session-key-BnWWjqNc.js"; import { n as normalizeAccountId } from "./account-id-CETVCrTz.js"; import { t as createDedupeCache } from "./dedupe-gst1CUro.js"; import { n as parseToolsBySenderTypedKey } from "./types.tools-_OkSyioO.js"; import { t as resolveAccountEntry } from "./account-lookup-CvFhSs3G.js"; import { n as normalizeMessageChannel } from "./message-channel-core-AaEWic-A.js"; import { n as resolveMergedAccountConfig } from "./channel-account-config-BHCmOzi1.js"; //#region src/config/group-policy.ts function resolveChannelGroupConfig(groups, groupId, caseInsensitive = false) { if (!groups) return; const direct = groups[groupId]; if (direct) return direct; if (!caseInsensitive) return; const target = normalizeLowercaseStringOrEmpty(groupId); const matchedKey = Object.keys(groups).find((key) => key !== "*" && normalizeLowercaseStringOrEmpty(key) === target); if (!matchedKey) return; return groups[matchedKey]; } const warnedLegacyToolsBySenderKeys = createDedupeCache({ ttlMs: 0, maxSize: 4096 }); const compiledToolsBySenderCache = /* @__PURE__ */ new WeakMap(); function normalizeSenderKey(value, options = {}) { const trimmed = value.trim(); if (!trimmed) return ""; const withoutAt = options.stripLeadingAt && trimmed.startsWith("@") ? trimmed.slice(1) : trimmed; return normalizeLowercaseStringOrEmpty(withoutAt); } function normalizeTypedSenderKey(value, type) { if (type === "channel") return normalizeChannelSenderKey(value); return normalizeSenderKey(value, { stripLeadingAt: type === "username" }); } function normalizeSenderPolicyChannel(value) { const trimmed = normalizeOptionalString(value); if (!trimmed) return ""; return normalizeMessageChannel(trimmed) ?? normalizeSenderKey(trimmed); } function normalizeChannelSenderKey(value) { const trimmed = value.trim(); const separatorIndex = trimmed.indexOf(":"); if (separatorIndex <= 0 || separatorIndex === trimmed.length - 1) return ""; const channel = normalizeSenderPolicyChannel(trimmed.slice(0, separatorIndex)); const senderId = normalizeTypedSenderKey(trimmed.slice(separatorIndex + 1), "id"); if (!channel || !senderId) return ""; return `${channel}:${senderId}`; } function normalizeLegacySenderKey(value) { return normalizeSenderKey(value, { stripLeadingAt: true }); } function warnLegacyToolsBySenderKey(rawKey) { const trimmed = rawKey.trim(); if (!trimmed || warnedLegacyToolsBySenderKeys.check(trimmed)) return; process.emitWarning(`toolsBySender key "${trimmed}" is deprecated. Use explicit prefixes (channel:, id:, e164:, username:, name:). Legacy unprefixed keys are matched as id only.`, { type: "DeprecationWarning", code: "OPENCLAW_TOOLS_BY_SENDER_UNTYPED_KEY" }); } function parseSenderPolicyKey(rawKey) { const trimmed = rawKey.trim(); if (!trimmed) return; if (trimmed === "*") return { kind: "wildcard" }; const typed = parseToolsBySenderTypedKey(trimmed); if (typed) { const key = normalizeTypedSenderKey(typed.value, typed.type); if (!key) return; return { kind: "typed", type: typed.type, key }; } warnLegacyToolsBySenderKey(trimmed); const key = normalizeLegacySenderKey(trimmed); if (!key) return; return { kind: "typed", type: "id", key }; } function createSenderPolicyBuckets() { return { channel: /* @__PURE__ */ new Map(), id: /* @__PURE__ */ new Map(), e164: /* @__PURE__ */ new Map(), username: /* @__PURE__ */ new Map(), name: /* @__PURE__ */ new Map() }; } function compileToolsBySenderPolicy(toolsBySender) { const entries = Object.entries(toolsBySender); if (entries.length === 0) return; const buckets = createSenderPolicyBuckets(); let wildcard; for (const [rawKey, policy] of entries) { if (!policy) continue; const parsed = parseSenderPolicyKey(rawKey); if (!parsed) continue; if (parsed.kind === "wildcard") { wildcard = policy; continue; } const bucket = buckets[parsed.type]; if (!bucket.has(parsed.key)) bucket.set(parsed.key, policy); } return { buckets, wildcard }; } function resolveCompiledToolsBySenderPolicy(toolsBySender) { const cached = compiledToolsBySenderCache.get(toolsBySender); if (cached) return cached; const compiled = compileToolsBySenderPolicy(toolsBySender); if (!compiled) return; compiledToolsBySenderCache.set(toolsBySender, compiled); return compiled; } function normalizeCandidate(value, type) { const trimmed = normalizeOptionalString(value); if (!trimmed) return ""; return normalizeTypedSenderKey(trimmed, type); } function normalizeSenderIdCandidates(value) { const trimmed = normalizeOptionalString(value); if (!trimmed) return []; const typed = normalizeTypedSenderKey(trimmed, "id"); const legacy = normalizeLegacySenderKey(trimmed); if (!typed) return legacy ? [legacy] : []; if (!legacy || legacy === typed) return [typed]; return [typed, legacy]; } function matchToolsBySenderPolicy(compiled, params) { const senderIdCandidates = normalizeSenderIdCandidates(params.senderId); const channel = normalizeSenderPolicyChannel(params.messageProvider); if (channel) for (const senderIdCandidate of senderIdCandidates) { const match = compiled.buckets.channel.get(`${channel}:${senderIdCandidate}`); if (match) return match; } for (const senderIdCandidate of senderIdCandidates) { const match = compiled.buckets.id.get(senderIdCandidate); if (match) return match; } const senderE164 = normalizeCandidate(params.senderE164, "e164"); if (senderE164) { const match = compiled.buckets.e164.get(senderE164); if (match) return match; } const senderUsername = normalizeCandidate(params.senderUsername, "username"); if (senderUsername) { const match = compiled.buckets.username.get(senderUsername); if (match) return match; } const senderName = normalizeCandidate(params.senderName, "name"); if (senderName) { const match = compiled.buckets.name.get(senderName); if (match) return match; } return compiled.wildcard; } function resolveToolsBySender(params) { const toolsBySender = params.toolsBySender; if (!toolsBySender) return; const compiled = resolveCompiledToolsBySenderPolicy(toolsBySender); if (!compiled) return; return matchToolsBySenderPolicy(compiled, params); } function resolveChannelGroups(cfg, channel, accountId) { const normalizedAccountId = normalizeAccountId(accountId); const channelConfig = cfg.channels?.[channel]; if (!channelConfig) return; return resolveMergedAccountConfig({ channelConfig, accounts: channelConfig.accounts, accountId: normalizedAccountId, inheritEmptyKeys: Object.keys(channelConfig.accounts ?? {}).length > 1 ? {} : { groups: "object" } }).groups; } /** Locate the authored map selected by the channel owner without changing its inheritance rules. */ function resolveChannelGroupsConfigPath(params) { const rootPath = `channels.${params.channel}.groups`; const accounts = (params.cfg.channels?.[params.channel])?.accounts; if (!accounts) return rootPath; const accountId = normalizeAccountId(params.accountId); const account = resolveAccountEntry(accounts, accountId); if (!account || params.groups !== void 0 && params.groups !== account.groups) return rootPath; const accountKey = Object.hasOwn(accounts, accountId) ? accountId : Object.keys(accounts).find((key) => accounts[key] === account); return accountKey ? `channels.${params.channel}.accounts[${JSON.stringify(accountKey)}].groups` : rootPath; } function resolveChannelGroupPolicyMode(cfg, channel, accountId) { const normalizedAccountId = normalizeAccountId(accountId); const channelConfig = cfg.channels?.[channel]; if (!channelConfig) return; return resolveAccountEntry(channelConfig.accounts, normalizedAccountId)?.groupPolicy ?? channelConfig.groupPolicy; } function resolveChannelGroupPolicy(params) { const { cfg, channel } = params; const groups = resolveChannelGroups(cfg, channel, params.accountId); const groupPolicy = resolveChannelGroupPolicyMode(cfg, channel, params.accountId); const hasGroups = Boolean(groups && Object.keys(groups).length > 0); const allowlistEnabled = groupPolicy === "allowlist" || hasGroups; const normalizedId = params.groupId?.trim(); const groupConfig = normalizedId ? resolveChannelGroupConfig(groups, normalizedId, params.groupIdCaseInsensitive) : void 0; const defaultConfig = groups?.["*"]; const allowAll = allowlistEnabled && Boolean(groups && Object.hasOwn(groups, "*")); const senderFilterBypass = groupPolicy === "allowlist" && !hasGroups && Boolean(params.hasGroupAllowFrom); return { allowlistEnabled, allowed: groupPolicy === "disabled" ? false : !allowlistEnabled || allowAll || Boolean(groupConfig) || senderFilterBypass, groupConfig, defaultConfig }; } function resolveChannelGroupRequireMention(params) { const { requireMentionOverride, overrideOrder = "after-config" } = params; const { groupConfig, defaultConfig } = resolveChannelGroupPolicy(params); const configMention = typeof groupConfig?.requireMention === "boolean" ? groupConfig.requireMention : typeof defaultConfig?.requireMention === "boolean" ? defaultConfig.requireMention : void 0; if (overrideOrder === "before-config" && typeof requireMentionOverride === "boolean") return requireMentionOverride; if (typeof configMention === "boolean") return configMention; if (overrideOrder !== "before-config" && typeof requireMentionOverride === "boolean") return requireMentionOverride; if (params.configuredGroupDefaultsToNoMention && groupConfig) return false; return true; } function resolveChannelGroupToolsPolicy(params) { const groups = resolveChannelGroups(params.cfg, params.channel, params.accountId); const groupIds = [params.groupId, ...Array.isArray(params.groupIdCandidates) ? params.groupIdCandidates : []]; let groupConfig; for (const rawGroupId of groupIds) { const groupId = rawGroupId?.trim(); if (!groupId) continue; groupConfig = resolveChannelGroupConfig(groups, groupId, params.groupIdCaseInsensitive); if (groupConfig) break; } const defaultConfig = groups?.["*"]; const groupSenderPolicy = params.senderPolicyMode === "never" ? void 0 : resolveToolsBySender({ toolsBySender: groupConfig?.toolsBySender, messageProvider: params.messageProvider ?? params.channel, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); if (groupSenderPolicy) return groupSenderPolicy; if (groupConfig?.tools) return groupConfig.tools; const defaultSenderPolicy = params.senderPolicyMode === "never" ? void 0 : resolveToolsBySender({ toolsBySender: defaultConfig?.toolsBySender, messageProvider: params.messageProvider ?? params.channel, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); if (defaultSenderPolicy) return defaultSenderPolicy; if (defaultConfig?.tools) return defaultConfig.tools; } //#endregion export { resolveChannelGroupsConfigPath as a, resolveChannelGroups as i, resolveChannelGroupRequireMention as n, resolveToolsBySender as o, resolveChannelGroupToolsPolicy as r, resolveChannelGroupPolicy as t };