@sellable/install
Version:
One-command installer for Sellable MCP in Claude Code, Codex, and Hermes
154 lines (143 loc) • 5 kB
JavaScript
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("<", "<")
);
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, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
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;
}