mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(tasks): tolerate invalid flow timestamps
This commit is contained in:
@@ -172,6 +172,27 @@ describe("flows commands", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("shows TaskFlows with Date-invalid timestamps without crashing", async () => {
|
||||
await withTaskFlowCommandStateDir(async () => {
|
||||
const flow = createManagedTaskFlow({
|
||||
ownerKey: "agent:main:main",
|
||||
controllerId: "tests/flows-command",
|
||||
goal: "Inspect malformed flow timestamp",
|
||||
status: "running",
|
||||
createdAt: 100,
|
||||
updatedAt: 8_700_000_000_000_000,
|
||||
});
|
||||
|
||||
const runtime = createRuntime();
|
||||
await flowsShowCommand({ lookup: flow.flowId, json: false }, runtime);
|
||||
|
||||
const lines = vi.mocked(runtime.log).mock.calls.map(([line]) => String(line));
|
||||
expect(lines).toContain(`flowId: ${flow.flowId}`);
|
||||
expect(lines).toContain("createdAt: 1970-01-01T00:00:00.100Z");
|
||||
expect(lines).toContain("updatedAt: n/a");
|
||||
});
|
||||
});
|
||||
|
||||
it("sanitizes TaskFlow text output before printing to the terminal", async () => {
|
||||
await withTaskFlowCommandStateDir(async () => {
|
||||
const unsafeOwnerKey = "agent:main:\u001b[31mowner";
|
||||
|
||||
@@ -4,6 +4,7 @@ import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { getRuntimeConfig } from "../config/config.js";
|
||||
import { info } from "../globals.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { timestampMsToIsoString } from "../shared/number-coercion.js";
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
import { listTasksForFlowId } from "../tasks/runtime-internal.js";
|
||||
import { cancelFlowById, getFlowTaskSummary } from "../tasks/task-executor.js";
|
||||
@@ -50,6 +51,10 @@ function shortToken(value: string | undefined, maxChars = ID_PAD): string {
|
||||
return truncate(trimmed, maxChars);
|
||||
}
|
||||
|
||||
function formatFlowTimestamp(value: number | undefined | null): string {
|
||||
return timestampMsToIsoString(value) ?? "n/a";
|
||||
}
|
||||
|
||||
function formatFlowStatusCell(status: TaskFlowStatus, rich: boolean) {
|
||||
const padded = status.padEnd(STATUS_PAD);
|
||||
if (!rich) {
|
||||
@@ -228,11 +233,11 @@ export async function flowsShowCommand(
|
||||
`notify: ${flow.notifyPolicy}`,
|
||||
...(stateSummary ? [`state: ${safeFlowDisplayText(stateSummary)}`] : []),
|
||||
...(flow.cancelRequestedAt
|
||||
? [`cancelRequestedAt: ${new Date(flow.cancelRequestedAt).toISOString()}`]
|
||||
? [`cancelRequestedAt: ${formatFlowTimestamp(flow.cancelRequestedAt)}`]
|
||||
: []),
|
||||
`createdAt: ${new Date(flow.createdAt).toISOString()}`,
|
||||
`updatedAt: ${new Date(flow.updatedAt).toISOString()}`,
|
||||
`endedAt: ${flow.endedAt ? new Date(flow.endedAt).toISOString() : "n/a"}`,
|
||||
`createdAt: ${formatFlowTimestamp(flow.createdAt)}`,
|
||||
`updatedAt: ${formatFlowTimestamp(flow.updatedAt)}`,
|
||||
`endedAt: ${formatFlowTimestamp(flow.endedAt)}`,
|
||||
`tasks: ${taskSummary.total} total · ${taskSummary.active} active · ${taskSummary.failures} issues`,
|
||||
];
|
||||
for (const line of lines) {
|
||||
|
||||
Reference in New Issue
Block a user