@pushpalsdev/cli 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pushpals-cli.js +222 -48
- package/package.json +1 -1
- package/runtime/configs/default.toml +3 -1
- package/runtime/configs/local.example.toml +3 -1
- package/runtime/sandbox/.pushpals-remotebuddy-fallback.js +13 -1
- package/runtime/sandbox/apps/workerpals/src/execute_job.ts +420 -169
- package/runtime/sandbox/configs/default.toml +3 -1
- package/runtime/sandbox/configs/local.example.toml +3 -1
- package/runtime/sandbox/packages/shared/src/config.ts +26 -1
package/dist/pushpals-cli.js
CHANGED
|
@@ -37,6 +37,7 @@ var DEFAULT_SERVICE_MANAGER_MAX_RESTART_ATTEMPTS = 4;
|
|
|
37
37
|
var DEFAULT_SERVICE_MANAGER_STABLE_WINDOW_MS = 60000;
|
|
38
38
|
var DEFAULT_SERVICE_MANAGER_BASE_BACKOFF_MS = 2000;
|
|
39
39
|
var DEFAULT_SERVICE_MANAGER_MAX_BACKOFF_MS = 30000;
|
|
40
|
+
var WINDOWS_TASKKILL_TIMEOUT_MS = 5000;
|
|
40
41
|
function formatEmbeddedRuntimeHealthLines(health) {
|
|
41
42
|
if (!health)
|
|
42
43
|
return [];
|
|
@@ -165,7 +166,9 @@ class ServiceManager {
|
|
|
165
166
|
Bun.spawnSync(["taskkill", "/PID", String(pid), "/T", "/F"], {
|
|
166
167
|
stdin: "ignore",
|
|
167
168
|
stdout: "ignore",
|
|
168
|
-
stderr: "ignore"
|
|
169
|
+
stderr: "ignore",
|
|
170
|
+
timeout: WINDOWS_TASKKILL_TIMEOUT_MS,
|
|
171
|
+
killSignal: "SIGKILL"
|
|
169
172
|
});
|
|
170
173
|
} else {
|
|
171
174
|
existing.proc.kill("SIGKILL");
|
|
@@ -226,7 +229,9 @@ class ServiceManager {
|
|
|
226
229
|
Bun.spawnSync(["taskkill", "/PID", String(pid), "/T", "/F"], {
|
|
227
230
|
stdin: "ignore",
|
|
228
231
|
stdout: "ignore",
|
|
229
|
-
stderr: "ignore"
|
|
232
|
+
stderr: "ignore",
|
|
233
|
+
timeout: WINDOWS_TASKKILL_TIMEOUT_MS,
|
|
234
|
+
killSignal: "SIGKILL"
|
|
230
235
|
});
|
|
231
236
|
} else {
|
|
232
237
|
service.proc.kill("SIGKILL");
|
|
@@ -457,7 +462,8 @@ var DEFAULT_WORKERPALS_OUTPUT_MAX_CHARS = 192 * 1024;
|
|
|
457
462
|
var DEFAULT_WORKERPALS_OUTPUT_MAX_LINES = 600;
|
|
458
463
|
var DEFAULT_WORKERPALS_OUTPUT_MAX_HEAD_LINES = 120;
|
|
459
464
|
var DEFAULT_WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS = 180000;
|
|
460
|
-
var DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS =
|
|
465
|
+
var DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS = 90000;
|
|
466
|
+
var DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_BEHAVIOR = "retry_once";
|
|
461
467
|
var DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_DIFF_CHARS = 16000;
|
|
462
468
|
var DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_VALIDATION_OUTPUT_CHARS = 8000;
|
|
463
469
|
var DEFAULT_WORKERPALS_EXECUTOR = "openai_codex";
|
|
@@ -536,6 +542,13 @@ function asString(value, fallback) {
|
|
|
536
542
|
return value.trim();
|
|
537
543
|
return fallback;
|
|
538
544
|
}
|
|
545
|
+
function asQualityCriticTimeoutBehavior(value) {
|
|
546
|
+
const normalized = String(value ?? "").trim().toLowerCase().replace(/-/g, "_");
|
|
547
|
+
if (normalized === "skip" || normalized === "retry_once" || normalized === "block") {
|
|
548
|
+
return normalized;
|
|
549
|
+
}
|
|
550
|
+
return DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_BEHAVIOR;
|
|
551
|
+
}
|
|
539
552
|
function asBoolean(value, fallback) {
|
|
540
553
|
if (typeof value === "boolean")
|
|
541
554
|
return value;
|
|
@@ -830,6 +843,7 @@ function loadPushPalsConfig(options = {}) {
|
|
|
830
843
|
const workerOutputMaxHeadLines = Math.max(1, Math.min(workerOutputMaxLines, asInt(parseIntEnv("WORKERPALS_OUTPUT_MAX_HEAD_LINES") ?? workerNode.output_max_head_lines, DEFAULT_WORKERPALS_OUTPUT_MAX_HEAD_LINES)));
|
|
831
844
|
const workerQualityValidationStepTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS") ?? workerNode.quality_validation_step_timeout_ms, DEFAULT_WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS));
|
|
832
845
|
const workerQualityCriticTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS") ?? workerNode.quality_critic_timeout_ms, DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS));
|
|
846
|
+
const workerQualityCriticTimeoutBehavior = asQualityCriticTimeoutBehavior(process.env.WORKERPALS_QUALITY_CRITIC_TIMEOUT_BEHAVIOR ?? workerNode.quality_critic_timeout_behavior);
|
|
833
847
|
const workerQualitySoftPassOnExhausted = parseBoolEnv("WORKERPALS_QUALITY_SOFT_PASS_ON_EXHAUSTED") ?? asBoolean(workerNode.quality_soft_pass_on_exhausted, true);
|
|
834
848
|
const workerQualityScopeGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_SCOPE_GATE_ENABLED") ?? asBoolean(workerNode.quality_scope_gate_enabled, true);
|
|
835
849
|
const workerQualityValidationGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_VALIDATION_GATE_ENABLED") ?? asBoolean(workerNode.quality_validation_gate_enabled, true);
|
|
@@ -843,6 +857,7 @@ function loadPushPalsConfig(options = {}) {
|
|
|
843
857
|
return DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE;
|
|
844
858
|
return Math.max(0, Math.min(10, parsed));
|
|
845
859
|
})();
|
|
860
|
+
const workerQualityCriticModel = firstNonEmpty(process.env.WORKERPALS_QUALITY_CRITIC_MODEL, asString(workerNode.quality_critic_model, ""), "");
|
|
846
861
|
const workerQualityCriticMaxDiffChars = Math.max(256, Math.min(524288, asInt(parseIntEnv("WORKERPALS_QUALITY_CRITIC_MAX_DIFF_CHARS") ?? workerNode.quality_critic_max_diff_chars, DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_DIFF_CHARS)));
|
|
847
862
|
const workerQualityCriticMaxValidationOutputChars = Math.max(256, Math.min(524288, asInt(parseIntEnv("WORKERPALS_QUALITY_CRITIC_MAX_VALIDATION_OUTPUT_CHARS") ?? workerNode.quality_critic_max_validation_output_chars, DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_VALIDATION_OUTPUT_CHARS)));
|
|
848
863
|
const workerExecutorResultPrefix = (() => {
|
|
@@ -1132,8 +1147,10 @@ function loadPushPalsConfig(options = {}) {
|
|
|
1132
1147
|
qualityPublishGateEnabled: workerQualityPublishGateEnabled,
|
|
1133
1148
|
qualityValidationStepTimeoutMs: workerQualityValidationStepTimeoutMs,
|
|
1134
1149
|
qualityCriticTimeoutMs: workerQualityCriticTimeoutMs,
|
|
1150
|
+
qualityCriticTimeoutBehavior: workerQualityCriticTimeoutBehavior,
|
|
1135
1151
|
qualitySoftPassOnExhausted: workerQualitySoftPassOnExhausted,
|
|
1136
1152
|
qualityCriticMinScore: workerQualityCriticMinScore,
|
|
1153
|
+
qualityCriticModel: workerQualityCriticModel,
|
|
1137
1154
|
qualityCriticMaxDiffChars: workerQualityCriticMaxDiffChars,
|
|
1138
1155
|
qualityCriticMaxValidationOutputChars: workerQualityCriticMaxValidationOutputChars,
|
|
1139
1156
|
executorResultPrefix: workerExecutorResultPrefix,
|
|
@@ -1620,6 +1637,11 @@ var DEFAULT_RUNTIME_BOOT_TIMEOUT_MS = 90000;
|
|
|
1620
1637
|
var DEFAULT_RUNTIME_BOOT_POLL_MS = 1000;
|
|
1621
1638
|
var DEFAULT_SERVER_BOOT_TIMEOUT_MS = 20000;
|
|
1622
1639
|
var DEFAULT_SERVICE_STABILITY_GRACE_MS = 4000;
|
|
1640
|
+
var DEFAULT_COMMAND_OUTPUT_DRAIN_TIMEOUT_MS = 2000;
|
|
1641
|
+
var DEFAULT_COMMAND_OUTPUT_MAX_CHARS = 512000;
|
|
1642
|
+
var DEFAULT_REMOTEBUDDY_SILENT_STARTUP_FALLBACK_MS = 20000;
|
|
1643
|
+
var WINDOWS_TASKKILL_TIMEOUT_MS2 = 5000;
|
|
1644
|
+
var RUNTIME_BINARY_DOWNLOAD_ATTEMPTS = 3;
|
|
1623
1645
|
var DEFAULT_STARTUP_GIT_PROBE_TIMEOUT_MS = 5000;
|
|
1624
1646
|
var DEFAULT_STARTUP_GIT_REMOTE_TIMEOUT_MS = 1e4;
|
|
1625
1647
|
var EMBEDDED_SERVICE_RESTART_MAX_ATTEMPTS = 4;
|
|
@@ -1947,6 +1969,91 @@ async function withStartupTimeout(promise, timeoutMs, timeoutValue) {
|
|
|
1947
1969
|
function jsonHtmlBootstrap(value) {
|
|
1948
1970
|
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
1949
1971
|
}
|
|
1972
|
+
function appendBoundedProcessOutput(existing, next, maxChars) {
|
|
1973
|
+
if (!next)
|
|
1974
|
+
return existing;
|
|
1975
|
+
const combined = `${existing}${next}`;
|
|
1976
|
+
if (combined.length <= maxChars)
|
|
1977
|
+
return combined;
|
|
1978
|
+
return combined.slice(combined.length - maxChars);
|
|
1979
|
+
}
|
|
1980
|
+
function createProcessOutputCollector(stream, maxChars = DEFAULT_COMMAND_OUTPUT_MAX_CHARS) {
|
|
1981
|
+
if (!stream) {
|
|
1982
|
+
return {
|
|
1983
|
+
done: Promise.resolve(""),
|
|
1984
|
+
snapshot: () => "",
|
|
1985
|
+
cancel: () => {}
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1988
|
+
const reader = stream.getReader();
|
|
1989
|
+
const decoder = new TextDecoder;
|
|
1990
|
+
let output = "";
|
|
1991
|
+
let cancelled = false;
|
|
1992
|
+
let finished = false;
|
|
1993
|
+
const done = (async () => {
|
|
1994
|
+
try {
|
|
1995
|
+
while (true) {
|
|
1996
|
+
const chunk = await reader.read();
|
|
1997
|
+
if (chunk.done)
|
|
1998
|
+
break;
|
|
1999
|
+
output = appendBoundedProcessOutput(output, decoder.decode(chunk.value, { stream: true }), maxChars);
|
|
2000
|
+
}
|
|
2001
|
+
output = appendBoundedProcessOutput(output, decoder.decode(), maxChars);
|
|
2002
|
+
} catch (err) {
|
|
2003
|
+
if (!cancelled) {
|
|
2004
|
+
output = appendBoundedProcessOutput(output, `
|
|
2005
|
+
[pushpals] output read failed: ${String(err)}`, maxChars);
|
|
2006
|
+
}
|
|
2007
|
+
} finally {
|
|
2008
|
+
finished = true;
|
|
2009
|
+
try {
|
|
2010
|
+
reader.releaseLock();
|
|
2011
|
+
} catch {}
|
|
2012
|
+
}
|
|
2013
|
+
return output;
|
|
2014
|
+
})();
|
|
2015
|
+
return {
|
|
2016
|
+
done,
|
|
2017
|
+
snapshot: () => output,
|
|
2018
|
+
cancel: () => {
|
|
2019
|
+
if (finished || cancelled)
|
|
2020
|
+
return;
|
|
2021
|
+
cancelled = true;
|
|
2022
|
+
try {
|
|
2023
|
+
reader.cancel().catch(() => {});
|
|
2024
|
+
} catch {}
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
async function finishProcessOutputCollector(collector, timeoutMs = DEFAULT_COMMAND_OUTPUT_DRAIN_TIMEOUT_MS) {
|
|
2029
|
+
const result = await Promise.race([
|
|
2030
|
+
collector.done.then((text) => ({ text, timedOut: false })),
|
|
2031
|
+
Bun.sleep(Math.max(1, timeoutMs)).then(() => ({
|
|
2032
|
+
text: collector.snapshot(),
|
|
2033
|
+
timedOut: true
|
|
2034
|
+
}))
|
|
2035
|
+
]);
|
|
2036
|
+
if (result.timedOut) {
|
|
2037
|
+
collector.cancel();
|
|
2038
|
+
}
|
|
2039
|
+
return result.text;
|
|
2040
|
+
}
|
|
2041
|
+
function terminateSpawnedProcessTree(proc, platform = process.platform) {
|
|
2042
|
+
try {
|
|
2043
|
+
const stopCommand = buildServiceStopCommand(proc.pid, platform);
|
|
2044
|
+
if (stopCommand) {
|
|
2045
|
+
Bun.spawnSync(stopCommand, {
|
|
2046
|
+
stdin: "ignore",
|
|
2047
|
+
stdout: "ignore",
|
|
2048
|
+
stderr: "ignore",
|
|
2049
|
+
timeout: WINDOWS_TASKKILL_TIMEOUT_MS2,
|
|
2050
|
+
killSignal: "SIGKILL"
|
|
2051
|
+
});
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
proc.kill("SIGKILL");
|
|
2055
|
+
} catch {}
|
|
2056
|
+
}
|
|
1950
2057
|
async function runCommandWithEnv(command, cwd, env, timeoutMs) {
|
|
1951
2058
|
try {
|
|
1952
2059
|
const proc = Bun.spawn(command, {
|
|
@@ -1955,32 +2062,37 @@ async function runCommandWithEnv(command, cwd, env, timeoutMs) {
|
|
|
1955
2062
|
stdout: "pipe",
|
|
1956
2063
|
stderr: "pipe"
|
|
1957
2064
|
});
|
|
2065
|
+
const stdoutCollector = createProcessOutputCollector(proc.stdout);
|
|
2066
|
+
const stderrCollector = createProcessOutputCollector(proc.stderr);
|
|
1958
2067
|
let timedOut = false;
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
} catch {}
|
|
1975
|
-
}, timeoutMs);
|
|
2068
|
+
const hasTimeout = typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0;
|
|
2069
|
+
let timeout = null;
|
|
2070
|
+
const exitCode = await Promise.race([
|
|
2071
|
+
proc.exited,
|
|
2072
|
+
hasTimeout ? new Promise((resolveTimeout) => {
|
|
2073
|
+
timeout = setTimeout(() => {
|
|
2074
|
+
timedOut = true;
|
|
2075
|
+
terminateSpawnedProcessTree(proc);
|
|
2076
|
+
resolveTimeout(-1);
|
|
2077
|
+
}, timeoutMs);
|
|
2078
|
+
}) : new Promise(() => {})
|
|
2079
|
+
]);
|
|
2080
|
+
if (timeout) {
|
|
2081
|
+
clearTimeout(timeout);
|
|
2082
|
+
timeout = null;
|
|
1976
2083
|
}
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2084
|
+
if (timedOut) {
|
|
2085
|
+
await Promise.race([
|
|
2086
|
+
proc.exited.catch(() => -1),
|
|
2087
|
+
Bun.sleep(DEFAULT_COMMAND_OUTPUT_DRAIN_TIMEOUT_MS)
|
|
2088
|
+
]);
|
|
2089
|
+
stdoutCollector.cancel();
|
|
2090
|
+
stderrCollector.cancel();
|
|
2091
|
+
}
|
|
2092
|
+
const [stdout, stderr] = await Promise.all([
|
|
2093
|
+
finishProcessOutputCollector(stdoutCollector),
|
|
2094
|
+
finishProcessOutputCollector(stderrCollector)
|
|
1981
2095
|
]);
|
|
1982
|
-
if (timer)
|
|
1983
|
-
clearTimeout(timer);
|
|
1984
2096
|
const normalizedStdout = stdout.trim();
|
|
1985
2097
|
const normalizedStderr = stderr.trim();
|
|
1986
2098
|
if (timedOut) {
|
|
@@ -2812,6 +2924,23 @@ function readLogTail(logPath, maxLines = 40) {
|
|
|
2812
2924
|
function hasStandaloneBunCrashSignature(text) {
|
|
2813
2925
|
return /\bpanic\(main thread\)|\bsegmentation fault\b|oh no:\s*bun has crashed\b/i.test(String(text ?? ""));
|
|
2814
2926
|
}
|
|
2927
|
+
function hasRemoteBuddyRuntimeOutput(logText) {
|
|
2928
|
+
return String(logText ?? "").split(/\r?\n/).some((line) => /\[(?:stdout|stderr)\]/i.test(line));
|
|
2929
|
+
}
|
|
2930
|
+
function shouldUseRemoteBuddySilentStartupFallback(opts) {
|
|
2931
|
+
const platform = opts.platform ?? process.platform;
|
|
2932
|
+
const thresholdMs = Math.max(1, opts.thresholdMs ?? DEFAULT_REMOTEBUDDY_SILENT_STARTUP_FALLBACK_MS);
|
|
2933
|
+
if (platform !== "win32")
|
|
2934
|
+
return false;
|
|
2935
|
+
if (!opts.fallbackAvailable)
|
|
2936
|
+
return false;
|
|
2937
|
+
if (opts.elapsedMs < thresholdMs)
|
|
2938
|
+
return false;
|
|
2939
|
+
const logText = String(opts.logText ?? "");
|
|
2940
|
+
if (hasStandaloneBunCrashSignature(logText))
|
|
2941
|
+
return false;
|
|
2942
|
+
return !hasRemoteBuddyRuntimeOutput(logText);
|
|
2943
|
+
}
|
|
2815
2944
|
function extractRemoteBuddyAutonomousEngineState(logText) {
|
|
2816
2945
|
const text = String(logText ?? "");
|
|
2817
2946
|
if (!text)
|
|
@@ -2840,21 +2969,33 @@ function readRemoteBuddyAutonomousEngineState(logPath) {
|
|
|
2840
2969
|
async function downloadBinaryAsset(tag, assetName, outPath) {
|
|
2841
2970
|
console.log(`[pushpals] Downloading embedded runtime binary ${assetName} from ${tag}...`);
|
|
2842
2971
|
const url = `${GITHUB_RELEASE_URL}/${encodeURIComponent(tag)}/${assetName}`;
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
const fallback = await downloadBinaryAssetWithWindowsCurlFallback(url, outPath, err);
|
|
2854
|
-
if (fallback)
|
|
2972
|
+
let lastError = null;
|
|
2973
|
+
for (let attempt = 1;attempt <= RUNTIME_BINARY_DOWNLOAD_ATTEMPTS; attempt += 1) {
|
|
2974
|
+
try {
|
|
2975
|
+
const response = await fetchWithTimeout(url, { headers: GITHUB_HEADERS }, 60000);
|
|
2976
|
+
if (!response.ok) {
|
|
2977
|
+
throw new Error(`Failed to download ${assetName} from ${tag} (HTTP ${response.status})`);
|
|
2978
|
+
}
|
|
2979
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
2980
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
2981
|
+
await Bun.write(outPath, bytes);
|
|
2855
2982
|
return;
|
|
2856
|
-
|
|
2983
|
+
} catch (err) {
|
|
2984
|
+
try {
|
|
2985
|
+
const fallback = await downloadBinaryAssetWithWindowsCurlFallback(url, outPath, err);
|
|
2986
|
+
if (fallback)
|
|
2987
|
+
return;
|
|
2988
|
+
lastError = err;
|
|
2989
|
+
} catch (fallbackErr) {
|
|
2990
|
+
lastError = fallbackErr;
|
|
2991
|
+
}
|
|
2992
|
+
if (attempt < RUNTIME_BINARY_DOWNLOAD_ATTEMPTS) {
|
|
2993
|
+
console.warn(`[pushpals] Runtime binary download for ${assetName} failed on attempt ${attempt}/${RUNTIME_BINARY_DOWNLOAD_ATTEMPTS}; retrying...`);
|
|
2994
|
+
await Bun.sleep(1000 * attempt);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2857
2997
|
}
|
|
2998
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError ?? "unknown error"));
|
|
2858
2999
|
}
|
|
2859
3000
|
async function downloadBinaryAssetWithWindowsCurlFallback(url, outPath, cause) {
|
|
2860
3001
|
if (process.platform !== "win32")
|
|
@@ -2947,7 +3088,9 @@ function stopRuntimeServices(services) {
|
|
|
2947
3088
|
Bun.spawnSync(stopCommand, {
|
|
2948
3089
|
stdin: "ignore",
|
|
2949
3090
|
stdout: "ignore",
|
|
2950
|
-
stderr: "ignore"
|
|
3091
|
+
stderr: "ignore",
|
|
3092
|
+
timeout: WINDOWS_TASKKILL_TIMEOUT_MS2,
|
|
3093
|
+
killSignal: "SIGKILL"
|
|
2951
3094
|
});
|
|
2952
3095
|
} else {
|
|
2953
3096
|
service.proc.kill("SIGKILL");
|
|
@@ -2982,6 +3125,11 @@ async function stopRuntimeServicesGracefully(services, timeoutMs = 1e4) {
|
|
|
2982
3125
|
if (running.length === 0)
|
|
2983
3126
|
return;
|
|
2984
3127
|
const ordered = [...running].sort((a, b) => resolveGracefulShutdownPriority(a.name) - resolveGracefulShutdownPriority(b.name));
|
|
3128
|
+
if (process.platform === "win32") {
|
|
3129
|
+
stopRuntimeServices(ordered);
|
|
3130
|
+
await waitForRuntimeServicesExit(ordered, Math.min(1000, timeoutMs));
|
|
3131
|
+
return;
|
|
3132
|
+
}
|
|
2985
3133
|
const nonServer = ordered.filter((service) => service.name !== "server");
|
|
2986
3134
|
const server = ordered.filter((service) => service.name === "server");
|
|
2987
3135
|
for (const service of nonServer) {
|
|
@@ -4346,16 +4494,25 @@ async function autoStartRuntimeServices(opts) {
|
|
|
4346
4494
|
bundlePath: sandboxPaths.remotebuddyFallbackBundlePath
|
|
4347
4495
|
} : null;
|
|
4348
4496
|
let remoteBuddyFallbackActivated = false;
|
|
4349
|
-
const maybeActivateRemoteBuddyWindowsFallback = () => {
|
|
4497
|
+
const maybeActivateRemoteBuddyWindowsFallback = (reason = "crash") => {
|
|
4350
4498
|
if (remoteBuddyFallbackActivated || !remoteBuddySourceFallback)
|
|
4351
4499
|
return false;
|
|
4352
4500
|
const tail = readLogTail(serviceLogPaths.remotebuddy, 120);
|
|
4353
|
-
if (!hasStandaloneBunCrashSignature(tail))
|
|
4501
|
+
if (reason === "crash" && !hasStandaloneBunCrashSignature(tail))
|
|
4502
|
+
return false;
|
|
4503
|
+
if (reason === "silent_startup" && !shouldUseRemoteBuddySilentStartupFallback({
|
|
4504
|
+
logText: tail,
|
|
4505
|
+
elapsedMs: Date.now() - remoteBuddyPhaseStartedAt,
|
|
4506
|
+
platform: process.platform,
|
|
4507
|
+
fallbackAvailable: Boolean(remoteBuddySourceFallback)
|
|
4508
|
+
})) {
|
|
4354
4509
|
return false;
|
|
4510
|
+
}
|
|
4355
4511
|
remoteBuddyFallbackActivated = true;
|
|
4356
4512
|
lastReportedRemoteBuddyAutonomyState = "unknown";
|
|
4357
|
-
|
|
4358
|
-
|
|
4513
|
+
const fallbackReason = reason === "silent_startup" ? "produced no startup output on Windows" : "crashed on Windows";
|
|
4514
|
+
console.warn(`[pushpals] Embedded RemoteBuddy standalone binary ${fallbackReason}; retrying with source fallback under bun.`);
|
|
4515
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded remotebuddy standalone binary ${fallbackReason}; retrying with source fallback under bun.`);
|
|
4359
4516
|
replaceService("remotebuddy", [
|
|
4360
4517
|
remoteBuddySourceFallback.bunBin,
|
|
4361
4518
|
remoteBuddySourceFallback.bundlePath,
|
|
@@ -4507,6 +4664,10 @@ ${tail}` : ""}`);
|
|
|
4507
4664
|
const optionalServiceExitWarned = new Set;
|
|
4508
4665
|
while (Date.now() < deadline) {
|
|
4509
4666
|
reportRemoteBuddyAutonomousEngineState();
|
|
4667
|
+
if (maybeActivateRemoteBuddyWindowsFallback("silent_startup")) {
|
|
4668
|
+
await Bun.sleep(DEFAULT_RUNTIME_BOOT_POLL_MS);
|
|
4669
|
+
continue;
|
|
4670
|
+
}
|
|
4510
4671
|
for (const service of serviceManager.getServices()) {
|
|
4511
4672
|
if (service.exited) {
|
|
4512
4673
|
if (service.name === "remotebuddy" && maybeActivateRemoteBuddyWindowsFallback()) {
|
|
@@ -5471,12 +5632,16 @@ async function main() {
|
|
|
5471
5632
|
}
|
|
5472
5633
|
process.exit(1);
|
|
5473
5634
|
}
|
|
5474
|
-
const
|
|
5635
|
+
const shouldProbeWorkerpalStartupCapacity = Boolean(config.remotebuddy.autoSpawnWorkerpals);
|
|
5636
|
+
const workerpalCapacity = shouldProbeWorkerpalStartupCapacity ? await waitForWorkerpalCapacity({
|
|
5475
5637
|
serverUrl,
|
|
5476
5638
|
timeoutMs: resolveWorkerpalStartupReadinessProbeTimeoutMs(config),
|
|
5477
5639
|
ttlMs: config.remotebuddy.workerpalOnlineTtlMs
|
|
5478
|
-
})
|
|
5479
|
-
|
|
5640
|
+
}) : {
|
|
5641
|
+
ok: false,
|
|
5642
|
+
detail: "WorkerPal auto-spawn is disabled"
|
|
5643
|
+
};
|
|
5644
|
+
if (shouldProbeWorkerpalStartupCapacity && !workerpalCapacity.ok) {
|
|
5480
5645
|
console.warn(`[pushpals] WorkerPal readiness probe did not find idle capacity yet (${workerpalCapacity.detail}).`);
|
|
5481
5646
|
console.warn("[pushpals] Continuing startup; WorkerPal warmup may still be in progress and first task dispatch can be delayed.");
|
|
5482
5647
|
if (workerpalDockerPrecheck.status === "failed") {
|
|
@@ -5489,7 +5654,13 @@ async function main() {
|
|
|
5489
5654
|
const startupWorkerExecutionReadiness = workerpalCapacity.ok ? {
|
|
5490
5655
|
state: "ready",
|
|
5491
5656
|
detail: workerpalCapacity.detail
|
|
5492
|
-
} :
|
|
5657
|
+
} : !shouldProbeWorkerpalStartupCapacity ? describeWorkerExecutionReadiness({
|
|
5658
|
+
autoSpawnWorkerpals: false,
|
|
5659
|
+
requireDocker: Boolean(config.remotebuddy.workerpalDocker) && Boolean(config.remotebuddy.workerpalRequireDocker),
|
|
5660
|
+
dockerPrecheck: workerpalDockerPrecheck,
|
|
5661
|
+
onlineWorkers: 0,
|
|
5662
|
+
idleWorkers: 0
|
|
5663
|
+
}) : workerpalDockerPrecheck.status === "failed" ? describeWorkerExecutionReadiness({
|
|
5493
5664
|
autoSpawnWorkerpals: Boolean(config.remotebuddy.autoSpawnWorkerpals),
|
|
5494
5665
|
requireDocker: Boolean(config.remotebuddy.workerpalDocker) && Boolean(config.remotebuddy.workerpalRequireDocker),
|
|
5495
5666
|
dockerPrecheck: workerpalDockerPrecheck,
|
|
@@ -5700,8 +5871,10 @@ export {
|
|
|
5700
5871
|
waitForWorkerpalCapacity,
|
|
5701
5872
|
startEmbeddedMonitoringHub,
|
|
5702
5873
|
shutdownEmbeddedServiceManagerGracefully,
|
|
5874
|
+
shouldUseRemoteBuddySilentStartupFallback,
|
|
5703
5875
|
shouldRunEmbeddedRuntimeStartupPrechecks,
|
|
5704
5876
|
shouldRestartEmbeddedService,
|
|
5877
|
+
runCommandWithEnv,
|
|
5705
5878
|
resolveWorkerpalDockerProbe,
|
|
5706
5879
|
resolveWorkerExecutionReadiness,
|
|
5707
5880
|
resolveWindowsWhereExecutableCandidatesForEnv,
|
|
@@ -5727,6 +5900,7 @@ export {
|
|
|
5727
5900
|
isDockerCleanupTimeoutDetail,
|
|
5728
5901
|
isCliExitCommand,
|
|
5729
5902
|
injectMonitoringHubBootstrap,
|
|
5903
|
+
hasRemoteBuddyRuntimeOutput,
|
|
5730
5904
|
formatWorkerExecutionReadinessLines,
|
|
5731
5905
|
formatTimestampedCliLine,
|
|
5732
5906
|
formatSessionEventLine,
|
package/package.json
CHANGED
|
@@ -159,9 +159,11 @@ quality_publish_gate_enabled = true
|
|
|
159
159
|
# Browser/e2e validation commands get a longer built-in floor (10m) because they
|
|
160
160
|
# may need to start a dev server and run browser automation.
|
|
161
161
|
quality_validation_step_timeout_ms = 180000
|
|
162
|
-
quality_critic_timeout_ms =
|
|
162
|
+
quality_critic_timeout_ms = 90000
|
|
163
|
+
quality_critic_timeout_behavior = "retry_once" # skip | retry_once | block
|
|
163
164
|
quality_soft_pass_on_exhausted = true
|
|
164
165
|
quality_critic_min_score = 8.0
|
|
166
|
+
quality_critic_model = "" # Optional faster/smaller model override for the critic.
|
|
165
167
|
quality_critic_max_diff_chars = 16000
|
|
166
168
|
quality_critic_max_validation_output_chars = 8000
|
|
167
169
|
executor_result_prefix = "__PUSHPALS_OH_RESULT__ "
|
|
@@ -75,9 +75,11 @@ quality_publish_gate_enabled = true
|
|
|
75
75
|
# Browser/e2e validation commands get a longer built-in floor (10m) because they
|
|
76
76
|
# may need to start a dev server and run browser automation.
|
|
77
77
|
quality_validation_step_timeout_ms = 180000
|
|
78
|
-
quality_critic_timeout_ms =
|
|
78
|
+
quality_critic_timeout_ms = 90000
|
|
79
|
+
quality_critic_timeout_behavior = "retry_once" # skip | retry_once | block
|
|
79
80
|
quality_soft_pass_on_exhausted = true
|
|
80
81
|
quality_critic_min_score = 8.0
|
|
82
|
+
quality_critic_model = "" # Optional faster/smaller model override for the critic.
|
|
81
83
|
quality_critic_max_diff_chars = 16000
|
|
82
84
|
quality_critic_max_validation_output_chars = 8000
|
|
83
85
|
executor_result_prefix = "__PUSHPALS_OH_RESULT__ "
|
|
@@ -743,7 +743,8 @@ var DEFAULT_WORKERPALS_OUTPUT_MAX_CHARS = 192 * 1024;
|
|
|
743
743
|
var DEFAULT_WORKERPALS_OUTPUT_MAX_LINES = 600;
|
|
744
744
|
var DEFAULT_WORKERPALS_OUTPUT_MAX_HEAD_LINES = 120;
|
|
745
745
|
var DEFAULT_WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS = 180000;
|
|
746
|
-
var DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS =
|
|
746
|
+
var DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS = 90000;
|
|
747
|
+
var DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_BEHAVIOR = "retry_once";
|
|
747
748
|
var DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_DIFF_CHARS = 16000;
|
|
748
749
|
var DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_VALIDATION_OUTPUT_CHARS = 8000;
|
|
749
750
|
var DEFAULT_WORKERPALS_EXECUTOR = "openai_codex";
|
|
@@ -824,6 +825,13 @@ function asString(value, fallback) {
|
|
|
824
825
|
return value.trim();
|
|
825
826
|
return fallback;
|
|
826
827
|
}
|
|
828
|
+
function asQualityCriticTimeoutBehavior(value) {
|
|
829
|
+
const normalized = String(value ?? "").trim().toLowerCase().replace(/-/g, "_");
|
|
830
|
+
if (normalized === "skip" || normalized === "retry_once" || normalized === "block") {
|
|
831
|
+
return normalized;
|
|
832
|
+
}
|
|
833
|
+
return DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_BEHAVIOR;
|
|
834
|
+
}
|
|
827
835
|
function asBoolean(value, fallback) {
|
|
828
836
|
if (typeof value === "boolean")
|
|
829
837
|
return value;
|
|
@@ -1118,6 +1126,7 @@ function loadPushPalsConfig(options = {}) {
|
|
|
1118
1126
|
const workerOutputMaxHeadLines = Math.max(1, Math.min(workerOutputMaxLines, asInt(parseIntEnv("WORKERPALS_OUTPUT_MAX_HEAD_LINES") ?? workerNode.output_max_head_lines, DEFAULT_WORKERPALS_OUTPUT_MAX_HEAD_LINES)));
|
|
1119
1127
|
const workerQualityValidationStepTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS") ?? workerNode.quality_validation_step_timeout_ms, DEFAULT_WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS));
|
|
1120
1128
|
const workerQualityCriticTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS") ?? workerNode.quality_critic_timeout_ms, DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS));
|
|
1129
|
+
const workerQualityCriticTimeoutBehavior = asQualityCriticTimeoutBehavior(process.env.WORKERPALS_QUALITY_CRITIC_TIMEOUT_BEHAVIOR ?? workerNode.quality_critic_timeout_behavior);
|
|
1121
1130
|
const workerQualitySoftPassOnExhausted = parseBoolEnv("WORKERPALS_QUALITY_SOFT_PASS_ON_EXHAUSTED") ?? asBoolean(workerNode.quality_soft_pass_on_exhausted, true);
|
|
1122
1131
|
const workerQualityScopeGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_SCOPE_GATE_ENABLED") ?? asBoolean(workerNode.quality_scope_gate_enabled, true);
|
|
1123
1132
|
const workerQualityValidationGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_VALIDATION_GATE_ENABLED") ?? asBoolean(workerNode.quality_validation_gate_enabled, true);
|
|
@@ -1131,6 +1140,7 @@ function loadPushPalsConfig(options = {}) {
|
|
|
1131
1140
|
return DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE;
|
|
1132
1141
|
return Math.max(0, Math.min(10, parsed));
|
|
1133
1142
|
})();
|
|
1143
|
+
const workerQualityCriticModel = firstNonEmpty(process.env.WORKERPALS_QUALITY_CRITIC_MODEL, asString(workerNode.quality_critic_model, ""), "");
|
|
1134
1144
|
const workerQualityCriticMaxDiffChars = Math.max(256, Math.min(524288, asInt(parseIntEnv("WORKERPALS_QUALITY_CRITIC_MAX_DIFF_CHARS") ?? workerNode.quality_critic_max_diff_chars, DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_DIFF_CHARS)));
|
|
1135
1145
|
const workerQualityCriticMaxValidationOutputChars = Math.max(256, Math.min(524288, asInt(parseIntEnv("WORKERPALS_QUALITY_CRITIC_MAX_VALIDATION_OUTPUT_CHARS") ?? workerNode.quality_critic_max_validation_output_chars, DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_VALIDATION_OUTPUT_CHARS)));
|
|
1136
1146
|
const workerExecutorResultPrefix = (() => {
|
|
@@ -1420,8 +1430,10 @@ function loadPushPalsConfig(options = {}) {
|
|
|
1420
1430
|
qualityPublishGateEnabled: workerQualityPublishGateEnabled,
|
|
1421
1431
|
qualityValidationStepTimeoutMs: workerQualityValidationStepTimeoutMs,
|
|
1422
1432
|
qualityCriticTimeoutMs: workerQualityCriticTimeoutMs,
|
|
1433
|
+
qualityCriticTimeoutBehavior: workerQualityCriticTimeoutBehavior,
|
|
1423
1434
|
qualitySoftPassOnExhausted: workerQualitySoftPassOnExhausted,
|
|
1424
1435
|
qualityCriticMinScore: workerQualityCriticMinScore,
|
|
1436
|
+
qualityCriticModel: workerQualityCriticModel,
|
|
1425
1437
|
qualityCriticMaxDiffChars: workerQualityCriticMaxDiffChars,
|
|
1426
1438
|
qualityCriticMaxValidationOutputChars: workerQualityCriticMaxValidationOutputChars,
|
|
1427
1439
|
executorResultPrefix: workerExecutorResultPrefix,
|