mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(cli-runner): keep output tails utf8 safe (#108355)
* fix(cli-runner): keep output tails utf8 safe * fix(cli-runner): satisfy output tail byte narrowing * test(cli-runner): format utf8 tail regression * refactor(cli-runner): reuse UTF-8 tail helper --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
co-authored by
Peter Steinberger
parent
a719a851bb
commit
71bea81669
@@ -0,0 +1,32 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { appendCliOutputTail } from "./execute-output-buffer.js";
|
||||
|
||||
const CLI_RUNNER_OUTPUT_TAIL_BYTES = 64 * 1024;
|
||||
const REPLACEMENT_CHARACTER = String.fromCharCode(0xfffd);
|
||||
const MULTIBYTE_CHARACTER = String.fromCodePoint(0x1f642);
|
||||
|
||||
describe("appendCliOutputTail", () => {
|
||||
it("keeps large chunk tails UTF-8 safe when truncation starts inside a character", () => {
|
||||
const chunk = `${"x".repeat(10)}${MULTIBYTE_CHARACTER}${"y".repeat(
|
||||
CLI_RUNNER_OUTPUT_TAIL_BYTES - 3,
|
||||
)}`;
|
||||
|
||||
const output = appendCliOutputTail("", chunk);
|
||||
|
||||
expect(Buffer.byteLength(output)).toBeLessThanOrEqual(CLI_RUNNER_OUTPUT_TAIL_BYTES);
|
||||
expect(output).not.toContain(REPLACEMENT_CHARACTER);
|
||||
expect(output).toBe("y".repeat(CLI_RUNNER_OUTPUT_TAIL_BYTES - 3));
|
||||
});
|
||||
|
||||
it("keeps appended tails UTF-8 safe when rolling overflow starts inside a character", () => {
|
||||
const existingTail = `${"x".repeat(10)}${MULTIBYTE_CHARACTER}${"y".repeat(
|
||||
CLI_RUNNER_OUTPUT_TAIL_BYTES - 14,
|
||||
)}`;
|
||||
|
||||
const output = appendCliOutputTail(existingTail, "z".repeat(11));
|
||||
|
||||
expect(Buffer.byteLength(output)).toBeLessThanOrEqual(CLI_RUNNER_OUTPUT_TAIL_BYTES);
|
||||
expect(output).not.toContain(REPLACEMENT_CHARACTER);
|
||||
expect(output).toBe(`${"y".repeat(CLI_RUNNER_OUTPUT_TAIL_BYTES - 14)}${"z".repeat(11)}`);
|
||||
});
|
||||
});
|
||||
@@ -1,16 +1,7 @@
|
||||
import { truncateUtf8Suffix } from "../../utils/utf8-truncate.js";
|
||||
|
||||
const CLI_RUNNER_OUTPUT_TAIL_BYTES = 64 * 1024;
|
||||
|
||||
export function appendCliOutputTail(tail: Buffer, chunk: string): Buffer {
|
||||
if (!chunk) {
|
||||
return tail;
|
||||
}
|
||||
const chunkBuffer = Buffer.from(chunk);
|
||||
if (chunkBuffer.byteLength >= CLI_RUNNER_OUTPUT_TAIL_BYTES) {
|
||||
return Buffer.from(chunkBuffer.subarray(chunkBuffer.byteLength - CLI_RUNNER_OUTPUT_TAIL_BYTES));
|
||||
}
|
||||
const next = Buffer.concat([tail, chunkBuffer], tail.byteLength + chunkBuffer.byteLength);
|
||||
if (next.byteLength <= CLI_RUNNER_OUTPUT_TAIL_BYTES) {
|
||||
return next;
|
||||
}
|
||||
return Buffer.from(next.subarray(next.byteLength - CLI_RUNNER_OUTPUT_TAIL_BYTES));
|
||||
export function appendCliOutputTail(tail: string, chunk: string): string {
|
||||
return truncateUtf8Suffix(`${tail}${chunk}`, CLI_RUNNER_OUTPUT_TAIL_BYTES);
|
||||
}
|
||||
|
||||
@@ -1607,12 +1607,12 @@ export async function executePreparedCliRun(
|
||||
},
|
||||
})
|
||||
: null;
|
||||
let stdoutTail: Buffer = Buffer.alloc(0);
|
||||
let stdoutTail = "";
|
||||
let stdoutParseBuffer: Buffer = Buffer.alloc(0);
|
||||
let stdoutBytes = 0;
|
||||
const stdoutHash = crypto.createHash("sha256");
|
||||
let stdoutParseExceeded = false;
|
||||
let stderrTail: Buffer = Buffer.alloc(0);
|
||||
let stderrTail = "";
|
||||
let stderrParseBuffer: Buffer = Buffer.alloc(0);
|
||||
let stderrBytes = 0;
|
||||
const stderrHash = crypto.createHash("sha256");
|
||||
@@ -1760,9 +1760,9 @@ export async function executePreparedCliRun(
|
||||
}
|
||||
|
||||
const stdout = stdoutParseBuffer.toString("utf8").trim();
|
||||
const stdoutDiagnostic = stdoutTail.toString("utf8").trim();
|
||||
const stdoutDiagnostic = stdoutTail.trim();
|
||||
const stderr = stderrParseBuffer.toString("utf8").trim();
|
||||
const stderrDiagnostic = stderrTail.toString("utf8").trim();
|
||||
const stderrDiagnostic = stderrTail.trim();
|
||||
const processDiagnostics = {
|
||||
backendId: context.backendResolved.id,
|
||||
processReason: result.reason,
|
||||
|
||||
Reference in New Issue
Block a user