UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

142 lines (141 loc) 5.08 kB
import { a as writeRuntimeJson, r as defaultRuntime } from "./runtime-CF2WjnNZ.js"; import { t as formatCliCommand } from "./command-format-C7YfyMTd.js"; import "./http-body-D3IMwTJJ.js"; import { i as readResponseWithLimit } from "./http-response-body-CwT_cCNz.js"; import { t as formatDocsLink } from "./links-ClIwBcy4.js"; import { n as isRich, r as theme } from "./theme-vjDs9tao.js"; import { n as runCommandWithRuntime } from "./cli-utils-D1DAWB8d.js"; //#region src/commands/docs.ts const SEARCH_API = "https://docs.openclaw.ai/api/search"; const SEARCH_TIMEOUT_MS = 3e4; const DOCS_SEARCH_RESPONSE_MAX_BYTES = 8388608; function escapeMarkdown(text) { return text.replace(/[()[\]]/g, "\\$&"); } function buildMarkdown(query, results) { const lines = [`# Docs search: ${escapeMarkdown(query)}`, ""]; if (results.length === 0) { lines.push("_No results._"); return lines.join("\n"); } for (const item of results) { const title = escapeMarkdown(item.title); const snippet = item.snippet ? escapeMarkdown(item.snippet) : ""; const suffix = snippet ? ` - ${snippet}` : ""; lines.push(`- [${title}](${item.link})${suffix}`); } return lines.join("\n"); } function formatLinkLabel(link) { return link.replace(/^https?:\/\//i, ""); } function renderRichResults(query, results, runtime) { runtime.log(`${theme.heading("Docs search:")} ${theme.info(query)}`); if (results.length === 0) { runtime.log(theme.muted("No results.")); return; } for (const item of results) { const linkLabel = formatLinkLabel(item.link); const link = formatDocsLink(item.link, linkLabel); runtime.log(`${theme.muted("-")} ${theme.command(item.title)} ${theme.muted("(")}${link}${theme.muted(")")}`); if (item.snippet) runtime.log(` ${theme.muted(item.snippet)}`); } } async function renderMarkdown(markdown, runtime) { runtime.log(markdown.trimEnd()); } async function fetchDocsSearch(query) { const url = new URL(SEARCH_API); url.searchParams.set("q", query); const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), SEARCH_TIMEOUT_MS); try { const response = await fetch(url, { headers: { Accept: "application/json" }, signal: controller.signal }); if (!response.ok) { await response.body?.cancel().catch(() => void 0); throw new Error(`HTTP ${response.status}`); } const bytes = await readResponseWithLimit(response, DOCS_SEARCH_RESPONSE_MAX_BYTES, { onOverflow: ({ maxBytes }) => /* @__PURE__ */ new Error(`Docs search response exceeds ${maxBytes} bytes`) }); let payload; try { payload = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)); } catch (cause) { throw new Error("Docs search response is malformed JSON", { cause }); } return parseDocsSearchResults(payload.results); } finally { clearTimeout(timeout); } } function parseDocsSearchResults(raw) { if (!Array.isArray(raw)) return []; const results = []; for (const item of raw) { if (!item || typeof item !== "object") continue; const entry = item; if (typeof entry.title !== "string" || typeof entry.link !== "string") continue; results.push({ title: entry.title, link: entry.link, snippet: typeof entry.snippet === "string" && entry.snippet.trim() ? entry.snippet : void 0 }); } return results; } /** Search hosted docs, or print the docs homepage when no query is provided. */ async function docsSearchCommand(queryParts, runtime, options = {}) { const query = queryParts.join(" ").trim(); if (!query) { if (options.json) { writeRuntimeJson(runtime, { query: null, url: "https://docs.openclaw.ai/", results: [] }); return; } const docs = formatDocsLink("/", "docs.openclaw.ai"); if (isRich()) { runtime.log(`${theme.muted("Docs:")} ${docs}`); runtime.log(`${theme.muted("Search:")} ${formatCliCommand("openclaw docs \"your query\"")}`); } else { runtime.log("Docs: https://docs.openclaw.ai/"); runtime.log(`Search: ${formatCliCommand("openclaw docs \"your query\"")}`); } return; } let results; try { results = await fetchDocsSearch(query); } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`Docs search failed: ${message}`, { cause: error }); } if (options.json) { writeRuntimeJson(runtime, { query, results }); return; } if (isRich()) { renderRichResults(query, results, runtime); return; } await renderMarkdown(buildMarkdown(query, results), runtime); } //#endregion //#region src/cli/docs-cli.ts function registerDocsCli(program) { program.command("docs").description("Search the live OpenClaw docs").argument("[query...]", "Search query").option("--json", "Output JSON", false).addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/docs", "docs.openclaw.ai/cli/docs")}\n`).action(async (queryParts, opts) => { await runCommandWithRuntime(defaultRuntime, async () => { await docsSearchCommand(queryParts, defaultRuntime, { json: Boolean(opts.json) }); }); }); } //#endregion export { registerDocsCli };