UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

185 lines (184 loc) • 6.09 kB
import { F as resolveTimerTimeoutMs } from "./number-coercion-Dq1l5muL.mjs"; import { n as registerActiveProgressLine, r as unregisterActiveProgressLine, t as clearActiveProgressLine } from "./progress-line-DiTuCPbL.mjs"; import { c as visibleWidth, s as truncateToVisibleWidth } from "./ansi-6Z0Nu6M4.mjs"; import { r as theme } from "./theme-DzaUZY4q.mjs"; import { n as supportsOscProgress, t as createOscProgressController } from "./osc-progress-AOryXq1b.mjs"; import { log, spinner, symbol } from "@clack/prompts"; //#region src/cli/progress.ts /** Keep animated labels inside Clack's captured erase width. */ function createProgressSpinner(options, decorationColumns) { const { output } = options; const readColumns = () => Number.isFinite(output.columns) && output.columns > 0 ? Math.floor(output.columns) : void 0; let columns = readColumns(); let label = ""; let finished = false; const spin = spinner(options); const render = (message) => { label = message; const width = columns === void 0 ? void 0 : columns - decorationColumns; return theme.accent(width === void 0 || visibleWidth(label) <= width ? label : width <= 0 ? "" : `${truncateToVisibleWidth(label, width - 1)}…`); }; const resize = () => { const next = readColumns(); if (columns === void 0 || next === void 0 || next >= columns) return; columns = next; if (columns <= decorationColumns) spin.clear(); else spin.message(render(label)); }; return { start: (message) => { resize(); if (columns === void 0 || columns > decorationColumns) { if (columns !== void 0) output.on("resize", resize); spin.start(render(message)); } }, message: (message) => spin.message(render(message)), stop: (message) => { if (finished) return; finished = true; output.off("resize", resize); spin.clear(); if (message !== void 0) log.message([`${symbol("submit")} ${message}`], { output, spacing: 0, withGuide: false }); } }; } let activeProgress = 0; const noopReporter = { setLabel: () => {}, setPercent: () => {}, tick: () => {}, done: () => {} }; function createCliProgress(options) { if (options.enabled === false || activeProgress > 0) return noopReporter; const stream = options.stream ?? process.stderr; const isTty = stream.isTTY; const fallback = options.fallback; const allowLog = !isTty && fallback === "log"; if (!isTty && !allowLog) return noopReporter; const delayMs = resolveTimerTimeoutMs(options.delayMs, 0, 0); const canOsc = isTty && supportsOscProgress(process.env, isTty); const stdinIsRaw = process.stdin.isRaw; const wantsSpinner = fallback === void 0 || fallback === "spinner"; const allowSpinner = wantsSpinner && isTty && !stdinIsRaw; const allowLine = isTty && fallback === "line"; if (isTty && stdinIsRaw && wantsSpinner) return noopReporter; let started = false; let finished = false; let label = options.label; const total = options.total ?? null; let completed = 0; let percent = 0; let indeterminate = options.indeterminate ?? (options.total === void 0 || options.total === null); activeProgress += 1; if (isTty) registerActiveProgressLine(stream); const controller = canOsc ? createOscProgressController({ env: process.env, isTty: stream.isTTY, write: (chunk) => stream.write(chunk) }) : null; const spin = allowSpinner ? createProgressSpinner({ output: stream }, 7) : null; const renderLine = allowLine ? () => { if (!started) return; const suffix = indeterminate ? "" : ` ${percent}%`; clearActiveProgressLine(); stream.write(`${theme.accent(label)}${suffix}`); } : null; const renderLog = allowLog ? (() => { let lastLine = ""; let lastAt = 0; const throttleMs = 250; return () => { if (!started) return; const suffix = indeterminate ? "" : ` ${percent}%`; const nextLine = `${label}${suffix}`; const now = Date.now(); if (nextLine === lastLine && now - lastAt < throttleMs) return; lastLine = nextLine; lastAt = now; stream.write(`${nextLine}\n`); }; })() : null; let timer = null; const applyState = () => { if (!started || finished) return; if (controller) { if (indeterminate) controller.setIndeterminate(label); else controller.setPercent(label, percent); } spin?.message(label); renderLine?.(); renderLog?.(); }; const start = () => { if (started) return; started = true; spin?.start(label); applyState(); }; if (delayMs === 0) start(); else timer = setTimeout(start, delayMs); const setLabel = (next) => { label = next; applyState(); }; const setPercent = (nextPercent) => { percent = Math.max(0, Math.min(100, Math.round(nextPercent))); indeterminate = false; applyState(); }; const tick = (delta = 1) => { if (!total) return; completed = Math.min(total, completed + delta); const nextPercent = total > 0 ? Math.round(completed / total * 100) : 0; setPercent(nextPercent); }; const done = () => { if (finished) return; finished = true; if (timer) { clearTimeout(timer); timer = null; } if (started) { controller?.clear(); spin?.stop(""); clearActiveProgressLine(); } if (isTty) unregisterActiveProgressLine(stream); activeProgress = Math.max(0, activeProgress - 1); }; return { setLabel, setPercent, tick, done }; } /** Run async work with a progress reporter that is always stopped in finally. */ async function withProgress(options, work) { const progress = createCliProgress(options); try { return await work(progress); } finally { progress.done(); } } /** Run async work with a progress reporter plus a completed/total update adapter. */ async function withProgressTotals(options, work) { return await withProgress(options, async (progress) => { const update = ({ completed, total, label }) => { if (label) progress.setLabel(label); if (!Number.isFinite(total) || total <= 0) return; progress.setPercent(completed / total * 100); }; return await work(update, progress); }); } //#endregion export { withProgressTotals as i, createProgressSpinner as n, withProgress as r, createCliProgress as t };