






















@@ -12,6 +12,7 @@ const DEFAULT_WINDOWS_EXTENSION_CHUNK_SIZE = 8;
1212const DEFAULT_SHARD_HEARTBEAT_MS = 30_000;
1313const DEFAULT_SHARD_TIMEOUT_MS = 15 * 60_000;
1414const DEFAULT_SHARD_KILL_GRACE_MS = 5_000;
15+const DEFAULT_SPLIT_CORE_SHARD_CONCURRENCY = 4;
1516const FAST_LOCAL_CHECK_MIN_CPUS = 12;
1617const FAST_LOCAL_CHECK_MIN_MEMORY_BYTES = 48 * 1024 ** 3;
1718const EXTENSION_TS_CONFIG = "config/tsconfig/oxlint.extensions.json";
@@ -128,11 +129,13 @@ export function shouldRunOxlintShardsSerial({
128129return false;
129130}
130131const localCheckMode = env.OPENCLAW_LOCAL_CHECK_MODE?.trim().toLowerCase();
131-if (localCheckMode === "full" || localCheckMode === "fast") {
132-return false;
133-}
134-if (localCheckMode === "throttled" || localCheckMode === "low-memory") {
135-return true;
132+if (!isRemoteChangedGateEnv(env)) {
133+if (localCheckMode === "full" || localCheckMode === "fast") {
134+return false;
135+}
136+if (localCheckMode === "throttled" || localCheckMode === "low-memory") {
137+return true;
138+}
136139}
137140const resources = resolveHostResources(hostResources);
138141if (env.CI === "true" || env.GITHUB_ACTIONS === "true") {
@@ -147,6 +150,13 @@ export function shouldRunOxlintShardsSerial({
147150);
148151}
149152153+function isRemoteChangedGateEnv(env) {
154+return (
155+env.OPENCLAW_CHECK_CHANGED_REMOTE_CHILD === "1" ||
156+env.OPENCLAW_CHANGED_LANES_RAW_SYNC === "1"
157+);
158+}
159+150160function listExtensionEntries({ cwd, readDir }) {
151161let entries;
152162try {
@@ -241,24 +251,25 @@ export async function main(extraArgs = process.argv.slice(2), runtimeEnv = proce
241251if ((prepareResult.status ?? 1) !== 0) {
242252process.exitCode = prepareResult.status ?? 1;
243253} else {
244-const runSerial =
245-shardArgs.splitCore ||
246-shouldRunOxlintShardsSerial({
247- env,
248-platform: process.platform,
249-});
250-const results = runSerial
254+const shardConcurrency = resolveOxlintShardConcurrency({
255+ env,
256+platform: process.platform,
257+splitCore: shardArgs.splitCore,
258+});
259+const results = shardConcurrency <= 1
251260 ? await runShardsSerial({
252261entries: selectedShards,
253262 env,
254263extraArgs: shardArgs.oxlintArgs,
255264 runner,
256265})
257- : await Promise.all(
258-selectedShards.map((shard) =>
259-runShard({ env, extraArgs: shardArgs.oxlintArgs, runner, shard }),
260-),
261-);
266+ : await runShardsParallel({
267+concurrency: Math.min(shardConcurrency, selectedShards.length),
268+entries: selectedShards,
269+ env,
270+extraArgs: shardArgs.oxlintArgs,
271+ runner,
272+});
262273process.exitCode = results.find((status) => status !== 0) ?? 0;
263274}
264275} finally {
@@ -322,6 +333,32 @@ export function filterOxlintShards(shards, only) {
322333return shards.filter((shard) => only.has(shard.name) || only.has(shard.name.split(":")[0]));
323334}
324335336+export function resolveOxlintShardConcurrency({
337+ env = process.env,
338+ platform = process.platform,
339+ hostResources,
340+ splitCore = false,
341+} = {}) {
342+if (shouldRunOxlintShardsSerial({ env, platform, hostResources })) {
343+return 1;
344+}
345+346+const explicitConcurrency = resolvePositiveEnvInt(env, "OPENCLAW_OXLINT_SHARD_CONCURRENCY");
347+if (explicitConcurrency !== null) {
348+return explicitConcurrency;
349+}
350+351+if (!splitCore) {
352+return Number.MAX_SAFE_INTEGER;
353+}
354+355+const resources = resolveHostResources(hostResources);
356+return Math.max(
357+1,
358+Math.min(DEFAULT_SPLIT_CORE_SHARD_CONCURRENCY, Math.floor(resources.logicalCpuCount / 4)),
359+);
360+}
361+325362async function runShardsSerial({ entries, env, extraArgs, runner }) {
326363const results = [];
327364for (const shard of entries) {
@@ -333,6 +370,30 @@ async function runShardsSerial({ entries, env, extraArgs, runner }) {
333370return results;
334371}
335372373+async function runShardsParallel({ concurrency, entries, env, extraArgs, runner }) {
374+const results = [];
375+results.length = entries.length;
376+let nextIndex = 0;
377+378+const workers = Array.from({ length: concurrency }, async () => {
379+for (;;) {
380+if (isParentTerminationRequested()) {
381+return;
382+}
383+const currentIndex = nextIndex;
384+nextIndex += 1;
385+const shard = entries[currentIndex];
386+if (!shard) {
387+return;
388+}
389+results[currentIndex] = await runShard({ env, extraArgs, runner, shard });
390+}
391+});
392+393+await Promise.all(workers);
394+return results.filter((status) => status !== undefined);
395+}
396+336397export async function runShard({ env, extraArgs, runner, shard }) {
337398console.error(`[oxlint:${shard.name}] starting`);
338399const startedAt = Date.now();
@@ -452,6 +513,16 @@ function resolveNonNegativeEnvInt(env, key, defaultValue) {
452513return Number.isFinite(parsedValue) && parsedValue >= 0 ? parsedValue : defaultValue;
453514}
454515516+function resolvePositiveEnvInt(env, key) {
517+const rawValue = env[key];
518+if (rawValue === undefined) {
519+return null;
520+}
521+522+const parsedValue = Number.parseInt(rawValue, 10);
523+return Number.isFinite(parsedValue) && parsedValue > 0 ? parsedValue : null;
524+}
525+455526function signalChildProcess({ child, signal, useProcessGroup }) {
456527if (!child.pid) {
457528return;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。