UNPKG

@sellable/install

Version:

One-command installer for Sellable MCP in Claude Code, Codex, and Hermes

154 lines (143 loc) • 5 kB
import eastAsianWidth from "eastasianwidth"; // This is the transport formatter in the pinned Hermes Slack adapter // (nousresearch/hermes-agent@sha256:1f983d...). Product uses it only to // reconcile a staged source message with the text Slack stores after native // Hermes delivery. The whole-image acceptance test compares this projection to // the real pinned adapter, so an upstream renderer change fails before release. const TABLE_SEPARATOR = /^\s*\|?\s*:?-+:?\s*(?:\|\s*:?-+:?\s*){1,}\|?\s*$/; const HTML_ENTITY = /&(amp|lt|gt);/g; const HTML_ENTITIES = Object.freeze({ amp: "&", lt: "<", gt: ">" }); function isTableRow(line) { const value = line.trim(); return value.length > 0 && value.includes("|"); } function splitTableRow(line) { let value = line.trim(); if (value.startsWith("|")) value = value.slice(1); if (value.endsWith("|")) value = value.slice(0, -1); return value.split("|").map((cell) => cell.trim()); } function displayWidth(value) { return [...value].reduce( (total, character) => total + (eastAsianWidth.eastAsianWidth(character) === "W" || eastAsianWidth.eastAsianWidth(character) === "F" ? 2 : 1), 0 ); } function alignTable(rows) { const parsed = rows.map(splitTableRow); const columnCount = Math.max(...parsed.map((row) => row.length)); for (const row of parsed) { while (row.length < columnCount) row.push(""); } parsed[1] = Array(columnCount).fill("---"); const widths = Array.from({ length: columnCount }, (_, column) => Math.max(...parsed.map((row) => displayWidth(row[column]))) ); return parsed.map((row, rowIndex) => { const cells = row.map((cell, column) => rowIndex === 1 ? "-".repeat(widths[column]) : cell + " ".repeat(Math.max(0, widths[column] - displayWidth(cell))) ); return `| ${cells.join(" | ")} |`; }); } function wrapMarkdownTables(text) { if (!text || !text.includes("|") || !text.includes("-")) return text; const lines = text.split("\n"); const output = []; let inFence = false; for (let index = 0; index < lines.length; ) { const line = lines[index]; if (line.trimStart().startsWith("```")) { inFence = !inFence; } else if ( !inFence && line.includes("|") && index + 1 < lines.length && TABLE_SEPARATOR.test(lines[index + 1]) ) { const table = [line, lines[index + 1]]; let next = index + 2; while (next < lines.length && isTableRow(lines[next])) { table.push(lines[next]); next += 1; } output.push("```", ...alignTable(table), "```"); index = next; continue; } output.push(line); index += 1; } return output.join("\n"); } /** Project standard Markdown into the exact text stored by pinned Hermes/Slack. */ export function renderHermesSlackMarkdown(content) { if (!content) return content; const placeholders = new Map(); let placeholderIndex = 0; const protect = (value) => { const key = `\0SL${placeholderIndex}\0`; placeholderIndex += 1; placeholders.set(key, value); return key; }; let text = wrapMarkdownTables(content).replace( /<!(?:everyone|channel|here)(?:\|[^>\n]*)?>/gi, (value) => value.replace("<", "&lt;") ); text = text.replace( /(```(?:[^\n]*\n)?[\s\S]*?```)/g, (block, _capture, offset, source) => { const lineStart = offset === 0 || source[offset - 1] === "\n"; return protect( lineStart ? block.replace(/^```[^\s`]+[ \t]*(\r?\n)/, "```$1") : block ); } ); text = text.replace(/(`[^`]+`)/g, (value) => protect(value)); text = text.replace( /(?<!!)\[([^\]]+)\]\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g, (_value, label, rawUrl) => { let url = rawUrl.trim(); if (url.startsWith("<") && url.endsWith(">")) { url = url.slice(1, -1).trim(); } return protect(`<${url}|${label}>`); } ); text = text.replace(/(<(?:[@#!]|(?:https?|mailto|tel):)[^>\n]+>)/g, (value) => protect(value) ); text = text.replace(/^(>+\s)/gm, (value) => protect(value)); text = text.replace(HTML_ENTITY, (_value, entity) => HTML_ENTITIES[entity]); text = text .replace(/&/g, "&amp;") .replace(/</g, "&lt;") .replace(/>/g, "&gt;"); text = text.replace(/^#{1,6}\s+(.+)$/gm, (_value, heading) => protect(`*${heading.trim().replace(/\*\*(.+?)\*\*/g, "$1")}*`) ); text = text.replace(/\*\*\*(.+?)\*\*\*/g, (_value, inner) => protect(`*_${inner}_*`) ); text = text.replace(/\*\*(.+?)\*\*/g, (_value, inner) => { const suffix = /[\p{L}\p{N}_]$/u.test(inner) ? "" : "\u200b"; return protect(`*${inner}${suffix}*`); }); text = text.replace( /(?<!\*)\*(\S(?:[^*\n]*?\S)?)\*(?!\*)/g, (_value, inner) => protect(`_${inner}_`) ); text = text.replace(/~~(.+?)~~/g, (_value, inner) => protect(`~${inner}~`)); for (const [key, value] of [...placeholders].reverse()) { text = text.split(key).join(value); } return text; }