


























@@ -19,6 +19,8 @@ const DEFAULT_OUTPUT_NAME = "openclaw-current.tgz";
1919const PACKAGE_URL_DOWNLOAD_TIMEOUT_MS = 60_000;
2020const PACKAGE_URL_MAX_BYTES = 250 * 1024 * 1024;
2121const PACKAGE_URL_MAX_REDIRECTS = 5;
22+const COMMAND_STDOUT_CAPTURE_MAX_CHARS = 8 * 1024 * 1024;
23+const COMMAND_STDERR_CAPTURE_MAX_CHARS = 128 * 1024;
2224const TRUSTED_PACKAGE_SOURCE_POLICY = ".github/package-trusted-sources.json";
2325const TRUSTED_PACKAGE_SOURCE_TOKEN_ENV = "OPENCLAW_TRUSTED_PACKAGE_TOKEN";
2426const BLOCKED_PACKAGE_HOSTNAMES = new Set([
@@ -145,44 +147,76 @@ function run(command, args, options = {}) {
145147 ...spawnOptions,
146148});
147149let timedOut = false;
150+let killTimer;
148151const timeout =
149152options.timeoutMs === undefined
150153 ? undefined
151154 : setTimeout(() => {
152155timedOut = true;
153156child.kill("SIGTERM");
154-setTimeout(() => child.kill("SIGKILL"), 5_000).unref?.();
157+killTimer = setTimeout(() => child.kill("SIGKILL"), 5_000);
158+killTimer.unref?.();
155159}, options.timeoutMs);
156160timeout?.unref?.();
157-let stdout = "";
158-let stderr = "";
161+let stdout = { text: "", truncatedChars: 0 };
162+let stderr = { text: "", truncatedChars: 0 };
159163if (options.capture) {
160164child.stdout.on("data", (chunk) => {
161-stdout += String(chunk);
165+stdout = appendBoundedCommandOutput(stdout, chunk, COMMAND_STDOUT_CAPTURE_MAX_CHARS);
162166});
163167child.stderr.on("data", (chunk) => {
164-stderr += String(chunk);
168+stderr = appendBoundedCommandOutput(stderr, chunk, COMMAND_STDERR_CAPTURE_MAX_CHARS);
165169});
166170}
167171child.on("error", reject);
168172child.on("close", (status, signal) => {
169173if (timeout) {
170174clearTimeout(timeout);
171175}
176+if (killTimer) {
177+clearTimeout(killTimer);
178+}
172179if (timedOut) {
173180reject(new Error(`${command} ${args.join(" ")} timed out after ${options.timeoutMs}ms`));
174181return;
175182}
176183if (status === 0) {
177-resolve(stdout);
184+if (stdout.truncatedChars > 0) {
185+reject(
186+new Error(
187+`${command} ${args.join(" ")} produced more than ${COMMAND_STDOUT_CAPTURE_MAX_CHARS} captured stdout chars`,
188+),
189+);
190+return;
191+}
192+resolve(stdout.text);
178193return;
179194}
180-const detail = stderr.trim() ? `\n${stderr.trim()}` : "";
195+const stderrText = formatCapturedCommandOutput(stderr).trim();
196+const detail = stderrText ? `\n${stderrText}` : "";
181197reject(new Error(`${command} ${args.join(" ")} failed with ${status ?? signal}${detail}`));
182198});
183199});
184200}
185201202+function appendBoundedCommandOutput(buffer, chunk, maxChars) {
203+const nextText = buffer.text + String(chunk);
204+if (nextText.length <= maxChars) {
205+return { text: nextText, truncatedChars: buffer.truncatedChars };
206+}
207+const truncatedChars = buffer.truncatedChars + nextText.length - maxChars;
208+return { text: nextText.slice(-maxChars), truncatedChars };
209+}
210+211+function formatCapturedCommandOutput(buffer) {
212+if (buffer.truncatedChars === 0) {
213+return buffer.text;
214+}
215+return `[output truncated ${buffer.truncatedChars} chars; showing tail]\n${buffer.text}`;
216+}
217+218+export const runCommandForTest = run;
219+186220async function walkFiles(dir) {
187221const entries = await fs.readdir(dir, { withFileTypes: true });
188222const files = [];
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。