UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

198 lines (197 loc) 7.71 kB
import { c as isRecord } from "./record-coerce-DItp3I4t.js"; import { isAlias, isMap, isNode, isScalar, parseDocument } from "yaml"; //#region packages/markdown-core/src/frontmatter.ts function stripQuotes(value) { const quote = value.at(0); return (quote === "\"" || quote === "'") && value.at(-1) === quote ? value.slice(1, -1) : value; } function coerceYamlFrontmatterValue(value) { if (value === null || value === void 0) return; if (typeof value === "string") return { value: value.trim(), kind: "scalar" }; if (typeof value === "number" || typeof value === "boolean") return { value: String(value), kind: "scalar" }; if (typeof value === "object") try { return { value: JSON.stringify(value), kind: "structured" }; } catch { return; } } function parseLineFrontmatter(block) { const result = {}; const lines = block.split("\n"); for (let i = 0; i < lines.length; i += 1) { const match = lines.at(i)?.match(/^([\w-]+):\s*(.*)$/); const key = match?.[1]; const rawValue = match?.[2]; if (!key || rawValue === void 0) continue; let value = rawValue.trim(); if (!value && /^[ \t]/.test(lines.at(i + 1) ?? "")) { const valueLines = []; while (i + 1 < lines.length) { const line = lines.at(i + 1); if (line === void 0 || line && !/^[ \t]/.test(line)) break; valueLines.push(line); i += 1; } value = valueLines.join("\n").trim(); } else value = stripQuotes(value); if (value) result[key] = value; } return result; } const FREEFORM_TEXT_FIELDS = /* @__PURE__ */ new Set([ "description", "read_when", "summary" ]); function normalizeFreeformFieldAtError(block) { const doc = parseDocument(block, { schema: "core", prettyErrors: false }); if (!isMap(doc.contents)) return block; const error = doc.errors.find((candidate) => candidate.pos?.[0] !== void 0); const descriptionAlias = doc.contents.items.find((candidate) => isScalar(candidate.key) && candidate.key.value === "description" && isAlias(candidate.value)); const pos = error?.pos?.[0] ?? (isNode(descriptionAlias?.key) ? descriptionAlias.key.range?.[0] : void 0); if (pos === void 0) return block; const lineStart = block.lastIndexOf("\n", pos) + 1; const lineEnd = block.indexOf("\n", pos); const end = lineEnd === -1 ? block.length : lineEnd; const match = block.slice(lineStart, end).match(/^(?:([\w-]+)|"([\w-]+)"|'([\w-]+)'):\s*(.*)$/); const keyName = match?.[1] ?? match?.[2] ?? match?.[3]; const rawValue = match?.[4]?.trim(); const isTopLevelField = doc.contents.items.some((pair) => isScalar(pair.key) && pair.key.value === keyName && pair.key.range?.[0] === lineStart); const recoverColonRichText = error?.code === "BLOCK_AS_IMPLICIT_KEY" && rawValue?.includes(": "); if (!keyName || !rawValue || !FREEFORM_TEXT_FIELDS.has(keyName) || !isTopLevelField || keyName !== "description" && !recoverColonRichText || /^[|>](?:[1-9][+-]?|[+-][1-9]?)?$/.test(rawValue)) return block; const replacement = `${keyName}: ${JSON.stringify(stripQuotes(rawValue))}`; return `${block.slice(0, lineStart)}${replacement}${block.slice(end)}`; } function parseYamlFrontmatterOnce(block, fallback) { try { const doc = parseDocument(block, { schema: "core", prettyErrors: false }); if (doc.errors.length > 0 || !isMap(doc.contents)) return { frontmatter: fallback, issues: doc.errors.length > 0 ? doc.errors.map((error) => ({ code: error.code ?? error.name, message: error.message })) : [{ code: "INVALID_ROOT", message: "frontmatter must be a YAML mapping" }] }; const parsed = doc.toJS(); if (!isRecord(parsed)) return { frontmatter: fallback, issues: [{ code: "INVALID_ROOT", message: "frontmatter must be a YAML mapping" }] }; const inlineColonKeys = /* @__PURE__ */ new Set(); for (const pair of doc.contents.items) { if (!isNode(pair.key)) continue; const start = pair.key.range?.[0]; if (start === void 0) continue; const lineEnd = block.indexOf("\n", start); const match = block.slice(start, lineEnd === -1 ? block.length : lineEnd).match(/^([\w-]+):\s*(.*)$/); if (match?.[1] && match[2]?.includes(":")) inlineColonKeys.add(match[1]); } const result = {}; for (const [rawKey, value] of Object.entries(parsed)) { const key = rawKey.trim(); const coerced = key ? coerceYamlFrontmatterValue(value) : void 0; if (!coerced) continue; const fallbackValue = Object.hasOwn(fallback, key) ? fallback[key] : void 0; result[key] = coerced.kind === "structured" && inlineColonKeys.has(key) && fallbackValue !== void 0 ? fallbackValue : coerced.value; } for (const [key, value] of Object.entries(fallback)) if (!Object.hasOwn(result, key)) result[key] = value; return { frontmatter: result, issues: [] }; } catch (error) { return { frontmatter: fallback, issues: [{ code: "YAML_EXCEPTION", message: error instanceof Error ? error.message : String(error) }] }; } } function parseYamlFrontmatter(block) { const fallback = parseLineFrontmatter(block); const parsed = parseYamlFrontmatterOnce(block, fallback); if (parsed.issues.length === 0) return parsed; let recoveredBlock = block; for (let i = 0; i < FREEFORM_TEXT_FIELDS.size; i += 1) { const next = normalizeFreeformFieldAtError(recoveredBlock); if (next === recoveredBlock) break; recoveredBlock = next; const reparsed = parseYamlFrontmatterOnce(recoveredBlock, fallback); if (reparsed.issues.length === 0) return reparsed; } return recoveredBlock === block ? parsed : parseYamlFrontmatterOnce(recoveredBlock, fallback); } function normalizeFrontmatterContent(content) { return content.replace(/^\uFEFF/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n"); } const FRONTMATTER_CLOSING_DELIMITER = /(?:^|\n)---[^\S\n]*(?:\n|(?![\s\S]))/; const FRONTMATTER_OPENING_DELIMITER = /^---[^\S\n]*\n/; function extractFrontmatterBlockFromNormalized(normalized) { const opening = FRONTMATTER_OPENING_DELIMITER.exec(normalized); if (!opening) return; const blockStart = opening[0].length; const tail = normalized.slice(blockStart); const closing = FRONTMATTER_CLOSING_DELIMITER.exec(tail); if (!closing) return; return { block: tail.slice(0, closing.index), body: tail.slice(closing.index + closing[0].length) }; } /** Splits a complete leading YAML frontmatter block from its Markdown body. */ function extractFrontmatterBlock(content) { return extractFrontmatterBlockFromNormalized(normalizeFrontmatterContent(content)); } /** Removes a leading YAML frontmatter block and returns the remaining Markdown body. */ function stripFrontmatterBlock(content) { const normalized = normalizeFrontmatterContent(content); return (extractFrontmatterBlockFromNormalized(normalized)?.body ?? normalized).trim(); } /** Parses leading YAML frontmatter into string values used by skill and metadata loaders. */ function parseFrontmatterBlock(content) { return parseFrontmatterBlockResult(content).frontmatter; } /** Parses frontmatter once while retaining recoverable YAML parser issues for owning loaders. */ function parseFrontmatterBlockResult(content) { const normalized = normalizeFrontmatterContent(content); const block = extractFrontmatterBlockFromNormalized(normalized)?.block; if (block !== void 0) return block ? parseYamlFrontmatter(block) : { frontmatter: {}, issues: [] }; return FRONTMATTER_OPENING_DELIMITER.test(normalized) ? { frontmatter: {}, issues: [{ code: "UNTERMINATED_FRONTMATTER", message: "missing closing --- delimiter" }] } : { frontmatter: {}, issues: [] }; } //#endregion export { stripFrontmatterBlock as i, parseFrontmatterBlock as n, parseFrontmatterBlockResult as r, extractFrontmatterBlock as t };