























@@ -7,12 +7,52 @@ import {
77assertOpenAiRequestLogUsed,
88} from "../agent-turn-output.mjs";
99import { assertOpenAiEnvAuthProfileStore } from "../auth-profile-store-assertions.mjs";
10+import { readPositiveIntEnv } from "../env-limits.mjs";
1011import { applyMockOpenAiModelConfig } from "../fixtures/mock-openai-config.mjs";
12+import { readTextFileTail } from "../text-file-utils.mjs";
11131214const command = process.argv[2];
13-const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8"));
15+const ERROR_DETAIL_TAIL_BYTES = 16 * 1024;
16+const JSON_ARTIFACT_MAX_BYTES = readPositiveIntEnv(
17+"OPENCLAW_NPM_ONBOARD_JSON_ARTIFACT_MAX_BYTES",
18+1024 * 1024,
19+);
20+const STATUS_TEXT_MAX_BYTES = readPositiveIntEnv(
21+"OPENCLAW_NPM_ONBOARD_STATUS_TEXT_MAX_BYTES",
22+1024 * 1024,
23+);
1424const ansiEscapePattern = new RegExp(String.raw`\u001b\[[0-?]*[ -/]*[@-~]`, "g");
152526+function readTextFileBounded(file, label, maxBytes) {
27+const stat = fs.statSync(file);
28+if (!stat.isFile()) {
29+throw new Error(`${label} is not a file: ${file}`);
30+}
31+if (stat.size > maxBytes) {
32+throw new Error(
33+`${label} exceeded ${maxBytes} bytes: ${file} (${stat.size} bytes). Tail: ${readTextFileTail(
34+ file,
35+ ERROR_DETAIL_TAIL_BYTES,
36+ )}`,
37+);
38+}
39+const text = fs.readFileSync(file, "utf8");
40+const bytes = Buffer.byteLength(text, "utf8");
41+if (bytes > maxBytes) {
42+throw new Error(
43+`${label} exceeded ${maxBytes} bytes: ${file} (${bytes} bytes). Tail: ${readTextFileTail(
44+ file,
45+ ERROR_DETAIL_TAIL_BYTES,
46+ )}`,
47+);
48+}
49+return text;
50+}
51+52+function readJson(file) {
53+return JSON.parse(readTextFileBounded(file, "JSON artifact", JSON_ARTIFACT_MAX_BYTES));
54+}
55+1656function stripAnsi(text) {
1757return text.replace(ansiEscapePattern, "");
1858}
@@ -198,6 +238,12 @@ function assertStatusSurfaces() {
198238const channelsStatusPath = process.argv[4];
199239const statusTextPath = process.argv[5];
200240const channelsStatus = readJson(channelsStatusPath);
241+const statusText = readTextFileBounded(
242+statusTextPath,
243+"plain status output",
244+STATUS_TEXT_MAX_BYTES,
245+);
246+const statusTail = readTextFileTail(statusTextPath, ERROR_DETAIL_TAIL_BYTES);
201247const configuredChannels = Array.isArray(channelsStatus.configuredChannels)
202248 ? channelsStatus.configuredChannels
203249 : [];
@@ -206,17 +252,20 @@ function assertStatusSurfaces() {
206252`channels status did not list configured channel ${channel}. Payload: ${JSON.stringify(channelsStatus)}`,
207253);
208254}
209-const statusText = fs.readFileSync(statusTextPath, "utf8");
210255if (!/channels/i.test(statusText)) {
211-throw new Error(`plain status output did not render a Channels section. Output: ${statusText}`);
256+throw new Error(
257+`plain status output did not render a Channels section. Output tail: ${statusTail}`,
258+);
212259}
213260const channelsSection = extractStatusSection(statusText, "channels");
214261if (!channelsSection) {
215-throw new Error(`plain status output did not render a Channels section. Output: ${statusText}`);
262+throw new Error(
263+`plain status output did not render a Channels section. Output tail: ${statusTail}`,
264+);
216265}
217266if (!channelsSection.toLowerCase().includes(channel.toLowerCase())) {
218267throw new Error(
219-`plain status output did not mention ${channel} in the Channels section. Output: ${statusText}`,
268+`plain status output did not mention ${channel} in the Channels section. Output tail: ${statusTail}`,
220269);
221270}
222271}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。