

















@@ -20,8 +20,14 @@ const DEPENDENCY_PATH_MARKERS = ["node_modules/", "openclaw-pnpm-node-modules/"]
2020const HASHED_ROOT_JS_RE = /^(?<base>.+)-[A-Za-z0-9_-]+\.js$/u;
2121const DEFAULT_CAPTURE_BYTES = 8 * 1024 * 1024;
2222const DEFAULT_HEARTBEAT_MS = 30_000;
23-const DEFAULT_TSDOWN_NODE_OPTIONS = "--max-old-space-size=8192";
2423const DEFAULT_TSDOWN_MAX_OLD_SPACE_MB = 8192;
24+const MIN_TSDOWN_MAX_OLD_SPACE_MB = 2048;
25+const TSDOWN_CGROUP_MEMORY_HEADROOM_MB = 768;
26+const CGROUP_MEMORY_LIMIT_PATHS = [
27+"/sys/fs/cgroup/memory.max",
28+"/sys/fs/cgroup/memory/memory.limit_in_bytes",
29+];
30+const PROC_MEMINFO_PATH = "/proc/meminfo";
2531const TERMINATION_GRACE_MS = 5_000;
2632const TSDOWN_OUTPUT_ROOTS = ["dist", "dist-runtime"];
2733const GENERATED_SOURCE_DECLARATION_PATHSPEC = ":(glob)extensions/**/*.d.ts";
@@ -202,7 +208,100 @@ function parseNonNegativeInteger(value) {
202208return Math.trunc(parsed);
203209}
204210205-function normalizeTsdownNodeOptions(nodeOptions) {
211+function parseCgroupMemoryLimitBytes(value) {
212+if (typeof value !== "string") {
213+return null;
214+}
215+const trimmed = value.trim();
216+if (trimmed === "" || trimmed === "max" || !/^\d+$/u.test(trimmed)) {
217+return null;
218+}
219+const parsed = BigInt(trimmed);
220+if (parsed <= 0n || parsed > BigInt(Number.MAX_SAFE_INTEGER)) {
221+return null;
222+}
223+return Number(parsed);
224+}
225+226+function readCgroupMemoryLimitBytes(params = {}) {
227+if (Number.isFinite(params.cgroupMemoryLimitBytes) && params.cgroupMemoryLimitBytes > 0) {
228+return Math.trunc(params.cgroupMemoryLimitBytes);
229+}
230+231+const fsImpl = params.fs ?? fs;
232+const paths = params.cgroupMemoryLimitPaths ?? CGROUP_MEMORY_LIMIT_PATHS;
233+for (const limitPath of paths) {
234+try {
235+const limitBytes = parseCgroupMemoryLimitBytes(fsImpl.readFileSync(limitPath, "utf8"));
236+if (limitBytes !== null) {
237+return limitBytes;
238+}
239+} catch {
240+// Missing cgroup files are expected outside Linux containers.
241+}
242+}
243+244+return null;
245+}
246+247+function parseProcMemTotalBytes(value) {
248+if (typeof value !== "string") {
249+return null;
250+}
251+const match = value.match(/^MemTotal:\s+(\d+)\s+kB$/imu);
252+if (!match) {
253+return null;
254+}
255+const parsed = BigInt(match[1]) * 1024n;
256+if (parsed <= 0n || parsed > BigInt(Number.MAX_SAFE_INTEGER)) {
257+return null;
258+}
259+return Number(parsed);
260+}
261+262+function readProcMemTotalBytes(params = {}) {
263+if (Number.isFinite(params.procMemTotalBytes) && params.procMemTotalBytes > 0) {
264+return Math.trunc(params.procMemTotalBytes);
265+}
266+267+const fsImpl = params.fs ?? fs;
268+try {
269+return parseProcMemTotalBytes(
270+fsImpl.readFileSync(params.procMeminfoPath ?? PROC_MEMINFO_PATH, "utf8"),
271+);
272+} catch {
273+return null;
274+}
275+}
276+277+function resolveTsdownMaxOldSpaceMb(params = {}) {
278+const limitBytes = readCgroupMemoryLimitBytes(params) ?? readProcMemTotalBytes(params);
279+if (limitBytes === null) {
280+return DEFAULT_TSDOWN_MAX_OLD_SPACE_MB;
281+}
282+283+const limitMb = Math.floor(limitBytes / 1024 / 1024);
284+if (limitMb <= 0) {
285+return DEFAULT_TSDOWN_MAX_OLD_SPACE_MB;
286+}
287+288+const cgroupCap = Math.max(
289+MIN_TSDOWN_MAX_OLD_SPACE_MB,
290+limitMb - TSDOWN_CGROUP_MEMORY_HEADROOM_MB,
291+);
292+return Math.min(DEFAULT_TSDOWN_MAX_OLD_SPACE_MB, cgroupCap);
293+}
294+295+function parseMaxOldSpaceSizeMb(value, fallbackMb) {
296+const parsed = Number(value);
297+if (!Number.isFinite(parsed) || parsed <= 0) {
298+return fallbackMb;
299+}
300+return Math.trunc(parsed);
301+}
302+303+function normalizeTsdownNodeOptions(nodeOptions, params = {}) {
304+const maxOldSpaceMb = resolveTsdownMaxOldSpaceMb(params);
206305const parts = nodeOptions.trim().split(/\s+/u).filter(Boolean);
207306const normalized = [];
208307let foundMaxOldSpaceSize = false;
@@ -212,18 +311,15 @@ function normalizeTsdownNodeOptions(nodeOptions) {
212311const inlineMatch = part.match(/^--max-old-space-size=(\d+)$/u);
213312if (inlineMatch) {
214313foundMaxOldSpaceSize = true;
215-const value = Math.max(Number(inlineMatch[1]), DEFAULT_TSDOWN_MAX_OLD_SPACE_MB);
314+const value = Math.min(parseMaxOldSpaceSizeMb(inlineMatch[1], maxOldSpaceMb), maxOldSpaceMb);
216315normalized.push(`--max-old-space-size=${value}`);
217316continue;
218317}
219318220319if (part === "--max-old-space-size") {
221320foundMaxOldSpaceSize = true;
222321const next = parts[index + 1];
223-const parsed = next === undefined ? Number.NaN : Number(next);
224-const value = Number.isFinite(parsed)
225- ? Math.max(Math.trunc(parsed), DEFAULT_TSDOWN_MAX_OLD_SPACE_MB)
226- : DEFAULT_TSDOWN_MAX_OLD_SPACE_MB;
322+const value = Math.min(parseMaxOldSpaceSizeMb(next, maxOldSpaceMb), maxOldSpaceMb);
227323normalized.push(`--max-old-space-size=${value}`);
228324if (next !== undefined) {
229325index += 1;
@@ -235,17 +331,17 @@ function normalizeTsdownNodeOptions(nodeOptions) {
235331}
236332237333if (!foundMaxOldSpaceSize) {
238-normalized.push(DEFAULT_TSDOWN_NODE_OPTIONS);
334+normalized.push(`--max-old-space-size=${maxOldSpaceMb}`);
239335}
240336241337return normalized.join(" ");
242338}
243339244-function resolveTsdownEnv(env) {
340+function resolveTsdownEnv(env, params = {}) {
245341const nodeOptions = env.NODE_OPTIONS?.trim() ?? "";
246342return {
247343 ...env,
248-NODE_OPTIONS: normalizeTsdownNodeOptions(nodeOptions),
344+NODE_OPTIONS: normalizeTsdownNodeOptions(nodeOptions, params),
249345};
250346}
251347@@ -292,7 +388,7 @@ export function createTsdownOutputScanner(params = {}) {
292388}
293389294390export function resolveTsdownBuildInvocation(params = {}) {
295-const env = resolveTsdownEnv(params.env ?? process.env);
391+const env = resolveTsdownEnv(params.env ?? process.env, params);
296392const tsdownArgs = [
297393"--config-loader",
298394"unrun",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。