UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

207 lines (206 loc) 6.67 kB
import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-mnp54Vah.js"; import { l as normalizeE164 } from "./utils-CCC-BEJH.js"; import "./string-coerce-runtime-CEGJWkQ_.js"; import { i as markdownToIR } from "./tables-BgtXxld3.js"; import { r as renderMarkdownIRChunksWithinLimit } from "./chunk-items-HPRB2OIa.js"; import "./text-chunking-D45TeeEs.js"; import "./text-utility-runtime-BZ3jGr-e.js"; import { t as resolveApprovalApprovers } from "./approval-approvers-CM-4zRvR.js"; import { t as createResolvedApproverActionAuthAdapter } from "./approval-auth-helpers-DU3OxkhL.js"; import { i as resolveSignalAccount } from "./accounts-ms-4J9iY.js"; import { d as normalizeSignalMessagingTarget, l as looksLikeUuid } from "./identity-IGz-X4Li.js"; //#region extensions/signal/src/approval-auth.ts function normalizeSignalApproverId(value) { const normalized = normalizeSignalMessagingTarget(String(value)); if (!normalized || normalized.startsWith("group:") || normalized.startsWith("username:")) return; if (looksLikeUuid(normalized)) return `uuid:${normalized}`; const e164 = normalizeE164(normalized); return e164.length > 1 ? e164 : void 0; } function getSignalApprovalApprovers(params) { const account = resolveSignalAccount(params).config; return resolveApprovalApprovers({ allowFrom: account.allowFrom, defaultTo: account.defaultTo, normalizeApprover: normalizeSignalApproverId }); } const signalApprovalAuth = createResolvedApproverActionAuthAdapter({ channelLabel: "Signal", resolveApprovers: getSignalApprovalApprovers, normalizeSenderId: (value) => normalizeSignalApproverId(value) }); //#endregion //#region extensions/signal/src/format.ts function normalizeUrlForComparison(url) { let normalized = normalizeLowercaseStringOrEmpty(url); normalized = normalized.replace(/^https?:\/\//, ""); normalized = normalized.replace(/^www\./, ""); normalized = normalized.replace(/\/+$/, ""); return normalized; } function mapStyle(style) { switch (style) { case "bold": return "BOLD"; case "italic": return "ITALIC"; case "strikethrough": return "STRIKETHROUGH"; case "code": case "code_block": return "MONOSPACE"; case "spoiler": return "SPOILER"; default: return null; } } function mergeStyles(styles) { const sorted = [...styles].toSorted((a, b) => { if (a.start !== b.start) return a.start - b.start; if (a.length !== b.length) return a.length - b.length; return a.style.localeCompare(b.style); }); const merged = []; for (const style of sorted) { const prev = merged[merged.length - 1]; if (prev && prev.style === style.style && style.start <= prev.start + prev.length) { const prevEnd = prev.start + prev.length; prev.length = Math.max(prevEnd, style.start + style.length) - prev.start; continue; } merged.push({ ...style }); } return merged; } function clampStyles(styles, maxLength) { const clamped = []; for (const style of styles) { const start = Math.max(0, Math.min(style.start, maxLength)); const length = Math.min(style.start + style.length, maxLength) - start; if (length > 0) clamped.push({ start, length, style: style.style }); } return clamped; } function applyInsertionsToStyles(spans, insertions) { if (insertions.length === 0) return spans; const sortedInsertions = [...insertions].toSorted((a, b) => a.pos - b.pos); let updated = spans; let cumulativeShift = 0; for (const insertion of sortedInsertions) { const insertionPos = insertion.pos + cumulativeShift; const next = []; for (const span of updated) { if (span.end <= insertionPos) { next.push(span); continue; } if (span.start >= insertionPos) { next.push({ start: span.start + insertion.length, end: span.end + insertion.length, style: span.style }); continue; } if (span.start < insertionPos && span.end > insertionPos) { if (insertionPos > span.start) next.push({ start: span.start, end: insertionPos, style: span.style }); const shiftedStart = insertionPos + insertion.length; const shiftedEnd = span.end + insertion.length; if (shiftedEnd > shiftedStart) next.push({ start: shiftedStart, end: shiftedEnd, style: span.style }); } } updated = next; cumulativeShift += insertion.length; } return updated; } function renderSignalText(ir) { const text = ir.text ?? ""; if (!text) return { text: "", styles: [] }; const sortedLinks = [...ir.links].toSorted((a, b) => a.start - b.start); let out = ""; let cursor = 0; const insertions = []; for (const link of sortedLinks) { if (link.start < cursor) continue; out += text.slice(cursor, link.end); const href = link.href.trim(); const trimmedLabel = text.slice(link.start, link.end).trim(); if (href) if (!trimmedLabel) { out += href; insertions.push({ pos: link.end, length: href.length }); } else { const normalizedLabel = normalizeUrlForComparison(trimmedLabel); let comparableHref = href; if (href.startsWith("mailto:")) comparableHref = href.slice(7); if (normalizedLabel !== normalizeUrlForComparison(comparableHref)) { const addition = ` (${href})`; out += addition; insertions.push({ pos: link.end, length: addition.length }); } } cursor = link.end; } out += text.slice(cursor); const adjusted = applyInsertionsToStyles(ir.styles.map((span) => { const mapped = mapStyle(span.style); if (!mapped) return null; return { start: span.start, end: span.end, style: mapped }; }).filter((span) => span !== null), insertions); const trimmedText = out.trimEnd(); const trimmedLength = trimmedText.length; return { text: trimmedText, styles: mergeStyles(clampStyles(adjusted.map((span) => ({ start: span.start, length: span.end - span.start, style: span.style })), trimmedLength)) }; } function markdownToSignalText(markdown, options = {}) { return renderSignalText(markdownToIR(markdown ?? "", { linkify: true, enableSpoilers: true, headingStyle: "bold", blockquotePrefix: "> ", tableMode: options.tableMode })); } function markdownToSignalTextChunks(markdown, limit, options = {}) { return renderMarkdownIRChunksWithinLimit({ ir: markdownToIR(markdown ?? "", { linkify: true, enableSpoilers: true, headingStyle: "bold", blockquotePrefix: "> ", tableMode: options.tableMode }), limit, renderChunk: renderSignalText, measureRendered: (rendered) => rendered.text.length }).map(({ rendered }) => rendered); } //#endregion export { signalApprovalAuth as i, markdownToSignalTextChunks as n, getSignalApprovalApprovers as r, markdownToSignalText as t };