From 8fef264f704f805ea3341714f81bc6aa6ca24eb7 Mon Sep 17 00:00:00 2001 From: Leon-SK668 <0668001470@xydigit.com> Date: Fri, 17 Jul 2026 09:20:34 +0800 Subject: [PATCH] fix(release): preserve emoji in ClawHub error details (#108172) * fix(release): keep ClawHub errors valid Unicode * fix(release): keep ClawHub helper source-resolvable --------- Co-authored-by: Peter Steinberger --- scripts/lib/plugin-clawhub-release.ts | 3 ++- test/plugin-clawhub-release.test.ts | 29 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/scripts/lib/plugin-clawhub-release.ts b/scripts/lib/plugin-clawhub-release.ts index 40eaf7ccefe0..95ffdee55319 100644 --- a/scripts/lib/plugin-clawhub-release.ts +++ b/scripts/lib/plugin-clawhub-release.ts @@ -1,6 +1,7 @@ // Plugin Clawhub Release script supports OpenClaw repository automation. import { execFileSync } from "node:child_process"; import { resolve } from "node:path"; +import { truncateUtf16Safe } from "../../packages/normalization-core/src/utf16-slice.js"; import { validateExternalCodePluginPackageJson } from "../../packages/plugin-package-contract/src/index.ts"; import { retryClawHubRead } from "../../src/infra/clawhub-retry.js"; import { readBoundedResponseText } from "./bounded-response.ts"; @@ -229,7 +230,7 @@ async function buildClawHubQueryError( body = ""; } if (body.length > CLAWHUB_ERROR_BODY_MAX_CHARS) { - body = `${body.slice(0, CLAWHUB_ERROR_BODY_MAX_CHARS)}...`; + body = `${truncateUtf16Safe(body, CLAWHUB_ERROR_BODY_MAX_CHARS)}...`; } const diagnosticHeaders = ["retry-after", "x-request-id", "x-vercel-id", "cf-ray"] .map((name) => { diff --git a/test/plugin-clawhub-release.test.ts b/test/plugin-clawhub-release.test.ts index 4f60a0eee1cd..bfcbddb3d181 100644 --- a/test/plugin-clawhub-release.test.ts +++ b/test/plugin-clawhub-release.test.ts @@ -794,6 +794,35 @@ describe("collectPluginClawHubReleasePlan", () => { expect(packageRequests).toBe(4); }); + it.each([ + { + caseName: "drops a split surrogate pair", + responseBody: `${"x".repeat(399)}\u{1f600}tail`, + expectedDetail: `${"x".repeat(399)}...`, + }, + { + caseName: "preserves a complete surrogate pair", + responseBody: `${"x".repeat(398)}\u{1f600}tail`, + expectedDetail: `${"x".repeat(398)}\u{1f600}...`, + }, + ])( + "keeps ClawHub error truncation UTF-16 safe: $caseName", + async ({ responseBody, expectedDetail }) => { + const repoDir = createTempPluginRepo(); + await expect( + collectPluginClawHubReleasePlan({ + rootDir: repoDir, + selection: ["@openclaw/demo-plugin"], + registryBaseUrl: "https://clawhub.ai", + fetchImpl: async () => new Response(responseBody, { status: 503 }), + sleep: async () => {}, + }), + ).rejects.toThrow( + `Failed to query ClawHub package @openclaw/demo-plugin: 503 ${expectedDetail}`, + ); + }, + ); + it("honors an HTTP-date Retry-After header", async () => { const repoDir = createTempPluginRepo(); const retryAfter = "Wed, 21 Oct 2030 07:28:00 GMT";