fix(cron): preserve UTF-16 table boundaries (#103616)

* fix(cron): preserve table Unicode boundaries

* refactor(cron): size table cells by display width

* docs(changelog): credit cron table fix

* fix(cron): sanitize Unicode table cells

* fix(cron): keep table truncation UTF-16 safe

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
mushuiyu886
2026-07-10 18:57:15 +01:00
committed by GitHub
co-authored by Peter Steinberger
parent fd2a2411b9
commit 5bb5e4fb0a
2 changed files with 18 additions and 2 deletions
+15
View File
@@ -90,6 +90,21 @@ describe("printCronList", () => {
expectLogsToInclude(logs, "isolated");
});
it.each([
["split surrogate", `${"x".repeat(20)}🚀tail`, `${"x".repeat(20)}...`],
["ASCII boundary", `${"x".repeat(21)}Atail`, `${"x".repeat(21)}...`],
["intact surrogate pair", `${"x".repeat(19)}🚀tail`, `${"x".repeat(19)}🚀...`],
])("keeps %s truncation UTF-16 safe", (_label, name, expected) => {
const { logs, runtime } = createRuntimeLogCapture();
printCronList([createBaseJob({ name })], runtime);
expectLogsToInclude(logs, expected);
const output = logs.join("\n");
expect(Buffer.from(output, "utf8").toString("utf8")).toBe(output);
expect(output).not.toContain("\uFFFD");
});
it("shows declaration metadata and existing run status", () => {
const job = createBaseJob({
declarationKey: "daily-report",
+3 -2
View File
@@ -7,6 +7,7 @@ import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
} from "@openclaw/normalization-core/string-coerce";
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
import { colorize, isRich, theme } from "../../../packages/terminal-core/src/theme.js";
import { listChannelPlugins } from "../../channels/plugins/index.js";
import { parseAbsoluteTimeMs } from "../../cron/parse.js";
@@ -360,9 +361,9 @@ const truncate = (value: string, width: number) => {
return value;
}
if (width <= 3) {
return value.slice(0, width);
return truncateUtf16Safe(value, width);
}
return `${value.slice(0, width - 3)}...`;
return `${truncateUtf16Safe(value, width - 3)}...`;
};
const formatIsoMinute = (iso: string) => {