openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
205 lines (204 loc) • 8.59 kB
JavaScript
import { R as timestampMsToIsoString } from "./number-coercion-CLj0HTDM.js";
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { a as writeRuntimeJson } from "./runtime-CF2WjnNZ.js";
import { n as getRuntimeConfig } from "./io.runtime-B9iJRs3w.js";
import { t as formatCliCommand } from "./command-format-C7YfyMTd.js";
import { n as sanitizeTerminalText } from "./safe-text-BGBqp1a4.js";
import "./config-Cs0XXL3x.js";
import { n as isRich, r as theme } from "./theme-vjDs9tao.js";
import { n as info } from "./globals-CTaGxEqj.js";
import { r as formatCliJsonFailure } from "./failure-output-Dc5ce9_F.js";
import { n as isTerminalTaskFlow, t as TASK_FLOW_STATUSES } from "./task-flow-registry.types-BidrdCoB.js";
import { $ as getTaskFlowById, k as listTasksForFlowId, st as resolveTaskFlowForLookupToken, tt as listTaskFlowRecords } from "./task-registry-BP9SCk1B.js";
import { n as formatTaskStatus, o as isTaskStatusIssue, r as formatTaskStatusDetail } from "./task-status-BLFHlu3t.js";
import "./runtime-internal-CdH7E8By.js";
import { d as getFlowTaskSummary, n as cancelFlowById } from "./task-executor-DHGV4r6b.js";
import { n as truncateUtf16WithEllipsis } from "./text-truncate-DL21A82B.js";
import { t as formatTextCell } from "./text-format-C-twF_E2.js";
import { t as parseCliEnumFilter } from "./enum-filter-DPEFp-pf.js";
import { t as formatTaskStatusCell } from "./task-status-cell-Cea5YdXI.js";
//#region src/commands/flows.ts
/** CLI commands for listing, inspecting, and cancelling TaskFlow records. */
const ID_PAD = 10;
const MODE_PAD = 14;
const REV_PAD = 6;
const CTRL_PAD = 20;
function formatFlowLookupMiss(lookup) {
return `TaskFlow not found: ${sanitizeTerminalText(lookup)}. Run ${formatCliCommand("openclaw tasks flow list")} to see recent flow ids.`;
}
function safeFlowDisplayText(value, maxChars) {
const sanitized = sanitizeTerminalText(value ?? "").trim();
if (!sanitized) return "n/a";
return typeof maxChars === "number" ? truncateUtf16WithEllipsis(sanitized, maxChars) : sanitized;
}
function shortToken(value, maxChars = ID_PAD) {
return safeFlowDisplayText(normalizeOptionalString(value), maxChars);
}
function formatFlowTimestamp(value) {
return timestampMsToIsoString(value) ?? "n/a";
}
function formatFlowRows(flows, rich) {
const header = [
"TaskFlow".padEnd(ID_PAD),
"Mode".padEnd(MODE_PAD),
"Status".padEnd(10),
"Rev".padEnd(REV_PAD),
"Controller".padEnd(CTRL_PAD),
"Tasks".padEnd(14),
"Goal"
].join(" ");
const lines = [rich ? theme.heading(header) : header];
for (const flow of flows) {
const taskSummary = getFlowTaskSummary(flow.flowId);
const counts = `${taskSummary.active} active/${taskSummary.total} total`;
lines.push([
shortToken(flow.flowId).padEnd(ID_PAD),
flow.syncMode.padEnd(MODE_PAD),
formatTaskStatusCell(flow.status, rich),
String(flow.revision).padEnd(REV_PAD),
formatTextCell(safeFlowDisplayText(flow.controllerId), CTRL_PAD),
counts.padEnd(14),
safeFlowDisplayText(flow.goal, 80)
].join(" "));
}
return lines;
}
function formatFlowListSummary(flows) {
const counts = {
active: 0,
waiting: 0,
blocked: 0,
issues: 0,
cancelRequested: 0
};
for (const flow of flows) {
counts.active += Number(flow.status === "queued" || flow.status === "running");
counts.waiting += Number(flow.status === "waiting");
counts.blocked += Number(flow.status === "blocked");
counts.issues += Number(flow.status === "failed" || flow.status === "lost");
counts.cancelRequested += Number(flow.cancelRequestedAt != null && !isTerminalTaskFlow(flow));
}
const waiting = counts.waiting ? ` · ${counts.waiting} waiting` : "";
const issues = counts.issues ? ` · ${counts.issues} issues` : "";
return `${counts.active} active${waiting} · ${counts.blocked} blocked${issues} · ${counts.cancelRequested} cancel-requested · ${flows.length} total`;
}
function summarizeWait(flow) {
if (flow.waitJson == null) return "n/a";
if (typeof flow.waitJson === "string" || typeof flow.waitJson === "number" || typeof flow.waitJson === "boolean") return String(flow.waitJson);
if (Array.isArray(flow.waitJson)) return `array(${flow.waitJson.length})`;
return Object.keys(flow.waitJson).toSorted().join(", ") || "object";
}
function summarizeFlowState(flow) {
if (flow.status === "blocked") {
if (flow.blockedSummary) return flow.blockedSummary;
if (flow.blockedTaskId) return `blocked by ${flow.blockedTaskId}`;
return "blocked";
}
if (flow.status === "waiting" && flow.waitJson != null) return summarizeWait(flow);
return null;
}
/** Lists TaskFlows with optional status filtering and JSON output. */
async function flowsListCommand(opts, runtime) {
const statusFilter = parseCliEnumFilter(opts.status, "--status", TASK_FLOW_STATUSES);
const flows = listTaskFlowRecords().filter((flow) => {
if (statusFilter && flow.status !== statusFilter) return false;
return true;
});
if (opts.json) {
writeRuntimeJson(runtime, {
count: flows.length,
status: statusFilter ?? null,
flows: flows.map((flow) => ({
...flow,
tasks: listTasksForFlowId(flow.flowId),
taskSummary: getFlowTaskSummary(flow.flowId)
}))
});
return;
}
runtime.log(info(`TaskFlows: ${flows.length}`));
runtime.log(info(`TaskFlow pressure: ${formatFlowListSummary(flows)}`));
if (statusFilter) runtime.log(info(`Status filter: ${sanitizeTerminalText(statusFilter)}`));
if (flows.length === 0) {
runtime.log(`No TaskFlows found. Run ${formatCliCommand("openclaw tasks list")} to inspect standalone background tasks.`);
return;
}
const rich = isRich();
for (const line of formatFlowRows(flows, rich)) runtime.log(line);
}
/** Shows one TaskFlow and its linked task summary. */
async function flowsShowCommand(opts, runtime) {
const flow = resolveTaskFlowForLookupToken(opts.lookup);
if (!flow) {
const message = formatFlowLookupMiss(opts.lookup);
if (opts.json) writeRuntimeJson(runtime, formatCliJsonFailure(message));
else runtime.error(message);
runtime.exit(1, opts.json ? { resetStream: process.stderr } : void 0);
return;
}
const tasks = listTasksForFlowId(flow.flowId);
const taskSummary = getFlowTaskSummary(flow.flowId);
const stateSummary = summarizeFlowState(flow);
if (opts.json) {
writeRuntimeJson(runtime, {
...flow,
tasks,
taskSummary
});
return;
}
const lines = [
"TaskFlow:",
`flowId: ${flow.flowId}`,
`status: ${flow.status}`,
`goal: ${safeFlowDisplayText(flow.goal)}`,
`currentStep: ${safeFlowDisplayText(flow.currentStep)}`,
`owner: ${safeFlowDisplayText(flow.ownerKey)}`,
`notify: ${flow.notifyPolicy}`,
...stateSummary ? [`state: ${safeFlowDisplayText(stateSummary)}`] : [],
...flow.cancelRequestedAt ? [`cancelRequestedAt: ${formatFlowTimestamp(flow.cancelRequestedAt)}`] : [],
`createdAt: ${formatFlowTimestamp(flow.createdAt)}`,
`updatedAt: ${formatFlowTimestamp(flow.updatedAt)}`,
`endedAt: ${formatFlowTimestamp(flow.endedAt)}`,
`tasks: ${taskSummary.total} total · ${taskSummary.active} active · ${tasks.filter(isTaskStatusIssue).length} issues`
];
for (const line of lines) runtime.log(sanitizeTerminalText(line));
if (tasks.length === 0) {
runtime.log("Linked tasks: none");
return;
}
runtime.log("Linked tasks:");
for (const task of tasks) {
const safeLabel = safeFlowDisplayText(task.label ?? task.task);
const detail = formatTaskStatusDetail(task);
const safeDetail = detail ? ` · ${safeFlowDisplayText(detail)}` : "";
runtime.log(sanitizeTerminalText(`- ${task.taskId} ${formatTaskStatus(task)} ${safeFlowDisplayText(task.runId)} ${safeLabel}${safeDetail}`));
}
}
/** Requests cancellation for one TaskFlow selected by id or lookup token. */
async function flowsCancelCommand(opts, runtime) {
const flow = resolveTaskFlowForLookupToken(opts.lookup);
if (!flow) {
runtime.error(formatFlowLookupMiss(opts.lookup));
runtime.exit(1);
return;
}
const result = await cancelFlowById({
cfg: getRuntimeConfig(),
flowId: flow.flowId
});
if (!result.found) {
runtime.error(sanitizeTerminalText(result.reason ?? formatFlowLookupMiss(opts.lookup)));
runtime.exit(1);
return;
}
if (!result.cancelled) {
runtime.error(sanitizeTerminalText(result.reason ?? `Could not cancel TaskFlow: ${opts.lookup}`));
runtime.exit(1);
return;
}
const updated = getTaskFlowById(flow.flowId) ?? result.flow ?? flow;
runtime.log(sanitizeTerminalText(`Cancelled ${updated.flowId} (${updated.syncMode}) with status ${updated.status}.`));
}
//#endregion
export { flowsCancelCommand, flowsListCommand, flowsShowCommand };