UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

270 lines (269 loc) • 11.2 kB
import { c as normalizeOptionalLowercaseString, g as readStringValue } from "./string-coerce-1VuiLaaR.mjs"; import { n as parseBooleanValue } from "./boolean-BEb0Fdt8.mjs"; import { c as isRecord, i as asOptionalObjectRecord } from "./record-coerce-TIsquoVR.mjs"; import { a as normalizeCsvOrLooseStringList } from "./string-normalization-CECOqf64.mjs"; import { t as parseJsonWithJson5Fallback } from "./parse-json-compat-BBtWoq5_.mjs"; import { t as MANIFEST_KEY } from "./legacy-names-Cq8ruaO5.mjs"; import JSON5 from "json5"; 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, document) { const doc = document ?? 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) { let doc; const failed = (issues) => ({ document: doc, result: { frontmatter: fallback, issues } }); try { doc = parseDocument(block, { schema: "core", prettyErrors: false }); if (doc.errors.length > 0 || !isMap(doc.contents)) return failed(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 failed([{ 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 { document: doc, result: { frontmatter: result, issues: [] } }; } catch (error) { return failed([{ code: "YAML_EXCEPTION", message: error instanceof Error ? error.message : String(error) }]); } } function parseYamlFrontmatter(block) { const fallback = parseLineFrontmatter(block); let parsed = parseYamlFrontmatterOnce(block, fallback); let recoveredBlock = block; for (let i = 0; i < FREEFORM_TEXT_FIELDS.size && parsed.result.issues.length > 0; i += 1) { const next = normalizeFreeformFieldAtError(recoveredBlock, parsed.document); if (next === recoveredBlock) break; recoveredBlock = next; parsed = parseYamlFrontmatterOnce(recoveredBlock, fallback); } return parsed.result; } 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 //#region src/shared/frontmatter.ts /** Reads a frontmatter field only when it is represented as a string value. */ function getFrontmatterString(frontmatter, key) { return readStringValue(frontmatter[key]); } /** Parses boolean frontmatter strings while preserving the caller's default for missing values. */ function parseFrontmatterBool(value, fallback) { const parsed = parseBooleanValue(value); return parsed === void 0 ? fallback : parsed; } /** Parses the JSON5 OpenClaw manifest block embedded inside a string frontmatter field. */ function resolveOpenClawManifestBlock(params) { const raw = getFrontmatterString(params.frontmatter, params.key ?? "metadata"); if (!raw) return; try { const parsed = asOptionalObjectRecord(parseJsonWithJson5Fallback(raw, JSON5)); if (!parsed) return; return asOptionalObjectRecord(parsed[MANIFEST_KEY]); } catch { return; } } /** Extracts normalized runtime requirement lists from an OpenClaw manifest block. */ function resolveOpenClawManifestRequires(metadataObj) { const requiresRaw = asOptionalObjectRecord(metadataObj.requires); if (!requiresRaw) return; return { bins: normalizeCsvOrLooseStringList(requiresRaw.bins), anyBins: normalizeCsvOrLooseStringList(requiresRaw.anyBins), env: normalizeCsvOrLooseStringList(requiresRaw.env), config: normalizeCsvOrLooseStringList(requiresRaw.config) }; } /** Parses manifest install entries with a caller-owned parser and drops unsupported specs. */ function resolveOpenClawManifestInstall(metadataObj, parseInstallSpec) { return (Array.isArray(metadataObj.install) ? metadataObj.install : []).map((entry) => parseInstallSpec(entry)).filter((entry) => Boolean(entry)); } /** Extracts normalized OS allowlist entries from an OpenClaw manifest block. */ function resolveOpenClawManifestOs(metadataObj) { return normalizeCsvOrLooseStringList(metadataObj.os); } /** Parses kind/type plus common install fields shared by package-manager install specs. */ function parseOpenClawManifestInstallBase(input, allowedKinds) { const raw = asOptionalObjectRecord(input); if (!raw) return; const kindRaw = typeof raw.kind === "string" ? raw.kind : typeof raw.type === "string" ? raw.type : ""; const kind = normalizeOptionalLowercaseString(kindRaw) ?? ""; if (!allowedKinds.includes(kind)) return; const spec = { raw, kind }; if (typeof raw.id === "string") spec.id = raw.id; if (typeof raw.label === "string") spec.label = raw.label; const bins = normalizeCsvOrLooseStringList(raw.bins); if (bins.length > 0) spec.bins = bins; return spec; } /** Copies optional common install fields onto a caller-specific install spec object. */ function applyOpenClawManifestInstallCommonFields(spec, parsed) { if (parsed.id) spec.id = parsed.id; if (parsed.label) spec.label = parsed.label; if (parsed.bins) spec.bins = parsed.bins; return spec; } //#endregion export { resolveOpenClawManifestBlock as a, resolveOpenClawManifestRequires as c, parseFrontmatterBlockResult as d, stripFrontmatterBlock as f, parseOpenClawManifestInstallBase as i, extractFrontmatterBlock as l, getFrontmatterString as n, resolveOpenClawManifestInstall as o, parseFrontmatterBool as r, resolveOpenClawManifestOs as s, applyOpenClawManifestInstallCommonFields as t, parseFrontmatterBlock as u };