@pushpalsdev/cli 1.1.4 → 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 +302 -117
- 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,13 @@ 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;
|
|
1645
|
+
var DEFAULT_STARTUP_GIT_PROBE_TIMEOUT_MS = 5000;
|
|
1646
|
+
var DEFAULT_STARTUP_GIT_REMOTE_TIMEOUT_MS = 1e4;
|
|
1623
1647
|
var EMBEDDED_SERVICE_RESTART_MAX_ATTEMPTS = 4;
|
|
1624
1648
|
var WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS = 15000;
|
|
1625
1649
|
var EMBEDDED_RUNTIME_SAFETY_CAP_DISABLE_ENV = "PUSHPALS_DISABLE_EMBEDDED_SAFETY_CAPS";
|
|
@@ -1917,9 +1941,119 @@ function parsePositiveInt(value, fallback) {
|
|
|
1917
1941
|
return fallback;
|
|
1918
1942
|
return parsed;
|
|
1919
1943
|
}
|
|
1944
|
+
function clampPositiveInt(value, min, max) {
|
|
1945
|
+
if (!Number.isFinite(value))
|
|
1946
|
+
return min;
|
|
1947
|
+
return Math.max(min, Math.min(max, Math.trunc(value)));
|
|
1948
|
+
}
|
|
1949
|
+
function resolveStartupGitProbeTimeoutMs(env) {
|
|
1950
|
+
return clampPositiveInt(parsePositiveInt(env.PUSHPALS_STARTUP_GIT_PROBE_TIMEOUT_MS, DEFAULT_STARTUP_GIT_PROBE_TIMEOUT_MS), 1000, 30000);
|
|
1951
|
+
}
|
|
1952
|
+
function resolveStartupGitRemoteTimeoutMs(env) {
|
|
1953
|
+
return clampPositiveInt(parsePositiveInt(env.PUSHPALS_STARTUP_GIT_REMOTE_TIMEOUT_MS, DEFAULT_STARTUP_GIT_REMOTE_TIMEOUT_MS), 1000, 60000);
|
|
1954
|
+
}
|
|
1955
|
+
async function withStartupTimeout(promise, timeoutMs, timeoutValue) {
|
|
1956
|
+
let timer = null;
|
|
1957
|
+
try {
|
|
1958
|
+
return await Promise.race([
|
|
1959
|
+
promise,
|
|
1960
|
+
new Promise((resolveTimeout) => {
|
|
1961
|
+
timer = setTimeout(() => resolveTimeout(timeoutValue()), Math.max(1, timeoutMs));
|
|
1962
|
+
})
|
|
1963
|
+
]);
|
|
1964
|
+
} finally {
|
|
1965
|
+
if (timer)
|
|
1966
|
+
clearTimeout(timer);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1920
1969
|
function jsonHtmlBootstrap(value) {
|
|
1921
1970
|
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
1922
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
|
+
}
|
|
1923
2057
|
async function runCommandWithEnv(command, cwd, env, timeoutMs) {
|
|
1924
2058
|
try {
|
|
1925
2059
|
const proc = Bun.spawn(command, {
|
|
@@ -1928,32 +2062,37 @@ async function runCommandWithEnv(command, cwd, env, timeoutMs) {
|
|
|
1928
2062
|
stdout: "pipe",
|
|
1929
2063
|
stderr: "pipe"
|
|
1930
2064
|
});
|
|
2065
|
+
const stdoutCollector = createProcessOutputCollector(proc.stdout);
|
|
2066
|
+
const stderrCollector = createProcessOutputCollector(proc.stderr);
|
|
1931
2067
|
let timedOut = false;
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
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;
|
|
2083
|
+
}
|
|
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();
|
|
1949
2091
|
}
|
|
1950
|
-
const [stdout, stderr
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
proc.exited
|
|
2092
|
+
const [stdout, stderr] = await Promise.all([
|
|
2093
|
+
finishProcessOutputCollector(stdoutCollector),
|
|
2094
|
+
finishProcessOutputCollector(stderrCollector)
|
|
1954
2095
|
]);
|
|
1955
|
-
if (timer)
|
|
1956
|
-
clearTimeout(timer);
|
|
1957
2096
|
const normalizedStdout = stdout.trim();
|
|
1958
2097
|
const normalizedStderr = stderr.trim();
|
|
1959
2098
|
if (timedOut) {
|
|
@@ -2000,15 +2139,15 @@ function withWindowsGitSchannelEnv(env, platform = process.platform) {
|
|
|
2000
2139
|
return env;
|
|
2001
2140
|
return appendGitConfigEnv(env, "http.sslBackend", "schannel");
|
|
2002
2141
|
}
|
|
2003
|
-
async function runGitWithEnv(args, cwd, env) {
|
|
2004
|
-
return await runCommandWithEnv(["git", ...args], cwd, withWindowsGitSchannelEnv(env));
|
|
2142
|
+
async function runGitWithEnv(args, cwd, env, timeoutMs) {
|
|
2143
|
+
return await runCommandWithEnv(["git", ...args], cwd, withWindowsGitSchannelEnv(env), timeoutMs);
|
|
2005
2144
|
}
|
|
2006
|
-
async function runGit(args, cwd) {
|
|
2145
|
+
async function runGit(args, cwd, timeoutMs) {
|
|
2007
2146
|
return await runGitWithEnv(args, cwd, {
|
|
2008
2147
|
...process.env,
|
|
2009
2148
|
GIT_TERMINAL_PROMPT: "0",
|
|
2010
2149
|
GCM_INTERACTIVE: "Never"
|
|
2011
|
-
});
|
|
2150
|
+
}, timeoutMs);
|
|
2012
2151
|
}
|
|
2013
2152
|
async function resolveCurrentGitRepoRoot(cwd) {
|
|
2014
2153
|
const inside = await runGit(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
@@ -2738,20 +2877,14 @@ function normalizeChildProcessEnv(baseEnv, platform = process.platform) {
|
|
|
2738
2877
|
}
|
|
2739
2878
|
return env;
|
|
2740
2879
|
}
|
|
2741
|
-
async function resolveCommandPath(command, cwd, env) {
|
|
2880
|
+
async function resolveCommandPath(command, cwd, env, timeoutMs = resolveStartupGitProbeTimeoutMs(env)) {
|
|
2742
2881
|
const lookupCommands = process.platform === "win32" ? resolveWindowsWhereExecutableCandidatesForEnv(env, process.platform).map((lookup) => [lookup, command]) : [["which", command]];
|
|
2743
2882
|
for (const lookup of lookupCommands) {
|
|
2744
2883
|
try {
|
|
2745
|
-
const
|
|
2746
|
-
|
|
2747
|
-
env,
|
|
2748
|
-
stdout: "pipe",
|
|
2749
|
-
stderr: "ignore"
|
|
2750
|
-
});
|
|
2751
|
-
const [stdout, exitCode] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
|
2752
|
-
if (exitCode !== 0)
|
|
2884
|
+
const result = await runCommandWithEnv(lookup, cwd, env, timeoutMs);
|
|
2885
|
+
if (!result.ok)
|
|
2753
2886
|
continue;
|
|
2754
|
-
const resolved = stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
|
|
2887
|
+
const resolved = result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
|
|
2755
2888
|
if (resolved)
|
|
2756
2889
|
return resolved;
|
|
2757
2890
|
} catch {}
|
|
@@ -2791,6 +2924,23 @@ function readLogTail(logPath, maxLines = 40) {
|
|
|
2791
2924
|
function hasStandaloneBunCrashSignature(text) {
|
|
2792
2925
|
return /\bpanic\(main thread\)|\bsegmentation fault\b|oh no:\s*bun has crashed\b/i.test(String(text ?? ""));
|
|
2793
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
|
+
}
|
|
2794
2944
|
function extractRemoteBuddyAutonomousEngineState(logText) {
|
|
2795
2945
|
const text = String(logText ?? "");
|
|
2796
2946
|
if (!text)
|
|
@@ -2819,21 +2969,33 @@ function readRemoteBuddyAutonomousEngineState(logPath) {
|
|
|
2819
2969
|
async function downloadBinaryAsset(tag, assetName, outPath) {
|
|
2820
2970
|
console.log(`[pushpals] Downloading embedded runtime binary ${assetName} from ${tag}...`);
|
|
2821
2971
|
const url = `${GITHUB_RELEASE_URL}/${encodeURIComponent(tag)}/${assetName}`;
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
const fallback = await downloadBinaryAssetWithWindowsCurlFallback(url, outPath, err);
|
|
2833
|
-
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);
|
|
2834
2982
|
return;
|
|
2835
|
-
|
|
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
|
+
}
|
|
2836
2997
|
}
|
|
2998
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError ?? "unknown error"));
|
|
2837
2999
|
}
|
|
2838
3000
|
async function downloadBinaryAssetWithWindowsCurlFallback(url, outPath, cause) {
|
|
2839
3001
|
if (process.platform !== "win32")
|
|
@@ -2926,7 +3088,9 @@ function stopRuntimeServices(services) {
|
|
|
2926
3088
|
Bun.spawnSync(stopCommand, {
|
|
2927
3089
|
stdin: "ignore",
|
|
2928
3090
|
stdout: "ignore",
|
|
2929
|
-
stderr: "ignore"
|
|
3091
|
+
stderr: "ignore",
|
|
3092
|
+
timeout: WINDOWS_TASKKILL_TIMEOUT_MS2,
|
|
3093
|
+
killSignal: "SIGKILL"
|
|
2930
3094
|
});
|
|
2931
3095
|
} else {
|
|
2932
3096
|
service.proc.kill("SIGKILL");
|
|
@@ -2961,6 +3125,11 @@ async function stopRuntimeServicesGracefully(services, timeoutMs = 1e4) {
|
|
|
2961
3125
|
if (running.length === 0)
|
|
2962
3126
|
return;
|
|
2963
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
|
+
}
|
|
2964
3133
|
const nonServer = ordered.filter((service) => service.name !== "server");
|
|
2965
3134
|
const server = ordered.filter((service) => service.name === "server");
|
|
2966
3135
|
for (const service of nonServer) {
|
|
@@ -3159,50 +3328,31 @@ function computeEmbeddedServiceRestartBackoffMs(attempt) {
|
|
|
3159
3328
|
function shouldRestartEmbeddedService(attempts, maxAttempts = EMBEDDED_SERVICE_RESTART_MAX_ATTEMPTS) {
|
|
3160
3329
|
return shouldRestartService(attempts, maxAttempts);
|
|
3161
3330
|
}
|
|
3162
|
-
async function canSpawnCommand(command, cwd, env) {
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
cwd,
|
|
3166
|
-
env,
|
|
3167
|
-
stdin: "ignore",
|
|
3168
|
-
stdout: "ignore",
|
|
3169
|
-
stderr: "ignore"
|
|
3170
|
-
});
|
|
3171
|
-
const exitCode = await proc.exited;
|
|
3172
|
-
return exitCode === 0;
|
|
3173
|
-
} catch {
|
|
3174
|
-
return false;
|
|
3175
|
-
}
|
|
3331
|
+
async function canSpawnCommand(command, cwd, env, timeoutMs = resolveStartupGitProbeTimeoutMs(env)) {
|
|
3332
|
+
const result = await runCommandWithEnv(command, cwd, env, timeoutMs);
|
|
3333
|
+
return result.ok;
|
|
3176
3334
|
}
|
|
3177
|
-
async function canSpawnGitViaWindowsShell(commandArgs, cwd, env, platform = process.platform) {
|
|
3335
|
+
async function canSpawnGitViaWindowsShell(commandArgs, cwd, env, platform = process.platform, timeoutMs = resolveStartupGitProbeTimeoutMs(env)) {
|
|
3178
3336
|
if (platform !== "win32")
|
|
3179
3337
|
return false;
|
|
3180
3338
|
const commandLine = commandArgs.map((arg) => quoteWindowsCmdArg(arg)).join(" ");
|
|
3181
3339
|
for (const shellExecutable of resolveWindowsShellExecutableCandidatesForEnv(env, platform)) {
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
env,
|
|
3186
|
-
stdin: "ignore",
|
|
3187
|
-
stdout: "ignore",
|
|
3188
|
-
stderr: "ignore"
|
|
3189
|
-
});
|
|
3190
|
-
const exitCode = await proc.exited;
|
|
3191
|
-
return exitCode === 0;
|
|
3192
|
-
} catch {}
|
|
3340
|
+
const result = await runCommandWithEnv([shellExecutable, "/d", "/s", "/c", commandLine], cwd, env, timeoutMs);
|
|
3341
|
+
if (result.ok)
|
|
3342
|
+
return true;
|
|
3193
3343
|
}
|
|
3194
3344
|
return false;
|
|
3195
3345
|
}
|
|
3196
|
-
async function resolveSourceControlManagerGitProbe(cwd, env, platform = process.platform) {
|
|
3346
|
+
async function resolveSourceControlManagerGitProbe(cwd, env, platform = process.platform, timeoutMs = resolveStartupGitProbeTimeoutMs(env)) {
|
|
3197
3347
|
const candidates = resolveRuntimeGitExecutableCandidates(env, platform);
|
|
3198
3348
|
for (const candidate of candidates) {
|
|
3199
|
-
if (await canSpawnCommand([candidate, "--version"], cwd, env)) {
|
|
3349
|
+
if (await canSpawnCommand([candidate, "--version"], cwd, env, timeoutMs)) {
|
|
3200
3350
|
return { ok: true, detail: candidate };
|
|
3201
3351
|
}
|
|
3202
3352
|
}
|
|
3203
3353
|
if (platform === "win32") {
|
|
3204
3354
|
for (const candidate of candidates) {
|
|
3205
|
-
if (await canSpawnGitViaWindowsShell([candidate, "--version"], cwd, env, platform)) {
|
|
3355
|
+
if (await canSpawnGitViaWindowsShell([candidate, "--version"], cwd, env, platform, timeoutMs)) {
|
|
3206
3356
|
return { ok: true, detail: `${candidate} via shell` };
|
|
3207
3357
|
}
|
|
3208
3358
|
}
|
|
@@ -3630,7 +3780,12 @@ async function precheckSourceControlManagerGitAvailability(opts) {
|
|
|
3630
3780
|
if (preconfiguredGitBinary) {
|
|
3631
3781
|
applyResolvedGitBinaryToRuntimeEnv(env, preconfiguredGitBinary, platform);
|
|
3632
3782
|
}
|
|
3633
|
-
const
|
|
3783
|
+
const remoteTimeoutMs = resolveStartupGitRemoteTimeoutMs(env);
|
|
3784
|
+
const remoteStatus = await withStartupTimeout(opts.gitRemoteCheckFn ? opts.gitRemoteCheckFn(opts.repoRoot, opts.remote, env) : opts.repoHasRemoteFn ? opts.repoHasRemoteFn(opts.repoRoot, opts.remote).then((hasRemote) => hasRemote ? { status: "ok", remote: opts.remote } : { status: "missing_remote", remote: opts.remote }) : checkGitRemoteConfigured(opts.repoRoot, opts.remote, env, remoteTimeoutMs), remoteTimeoutMs, () => ({
|
|
3785
|
+
status: "error",
|
|
3786
|
+
remote: opts.remote,
|
|
3787
|
+
detail: `timed out after ${remoteTimeoutMs}ms`
|
|
3788
|
+
}));
|
|
3634
3789
|
if (remoteStatus.status === "missing_remote") {
|
|
3635
3790
|
return {
|
|
3636
3791
|
status: "skipped",
|
|
@@ -3719,7 +3874,7 @@ function resolveWorkerpalStartupReadinessProbeTimeoutMs(config) {
|
|
|
3719
3874
|
function shouldRunEmbeddedRuntimeStartupPrechecks(opts) {
|
|
3720
3875
|
return !opts.serverHealthy && !opts.noAutoStart;
|
|
3721
3876
|
}
|
|
3722
|
-
async function checkGitRemoteConfigured(repoRoot, remote, env) {
|
|
3877
|
+
async function checkGitRemoteConfigured(repoRoot, remote, env, timeoutMs = resolveStartupGitRemoteTimeoutMs(env ?? process.env)) {
|
|
3723
3878
|
const normalizedRemote = String(remote ?? "").trim();
|
|
3724
3879
|
if (!normalizedRemote) {
|
|
3725
3880
|
return { status: "missing_remote", remote: normalizedRemote };
|
|
@@ -3728,7 +3883,7 @@ async function checkGitRemoteConfigured(repoRoot, remote, env) {
|
|
|
3728
3883
|
...process.env,
|
|
3729
3884
|
GIT_TERMINAL_PROMPT: "0",
|
|
3730
3885
|
GCM_INTERACTIVE: "Never"
|
|
3731
|
-
});
|
|
3886
|
+
}, timeoutMs);
|
|
3732
3887
|
if (result.ok && result.stdout) {
|
|
3733
3888
|
return { status: "ok", remote: normalizedRemote };
|
|
3734
3889
|
}
|
|
@@ -3757,7 +3912,7 @@ async function checkPushpalsBranchOnRemote(repoRoot, remote, branch) {
|
|
|
3757
3912
|
};
|
|
3758
3913
|
}
|
|
3759
3914
|
const ref = `refs/heads/${normalizedBranch}`;
|
|
3760
|
-
const result = await runGit(["ls-remote", "--heads", normalizedRemote, ref], repoRoot);
|
|
3915
|
+
const result = await runGit(["ls-remote", "--heads", normalizedRemote, ref], repoRoot, resolveStartupGitRemoteTimeoutMs(process.env));
|
|
3761
3916
|
if (!result.ok) {
|
|
3762
3917
|
const detail = result.stderr || result.stdout || `exit ${result.exitCode}`;
|
|
3763
3918
|
return {
|
|
@@ -3789,8 +3944,9 @@ async function enforcePushpalsRemoteBranchPrecheck(repoRoot, remote, branch) {
|
|
|
3789
3944
|
console.error("[pushpals] Precheck failed: create/push that branch first or set source_control_manager.pushpals_branch to an existing remote branch.");
|
|
3790
3945
|
return false;
|
|
3791
3946
|
}
|
|
3792
|
-
console.
|
|
3793
|
-
|
|
3947
|
+
console.warn(`[pushpals] Precheck warning: could not verify remote branch "${result.remote}/${result.branch}": ${result.detail}`);
|
|
3948
|
+
console.warn("[pushpals] Precheck warning: continuing startup without SourceControlManager branch verification because the remote check was inconclusive.");
|
|
3949
|
+
return true;
|
|
3794
3950
|
}
|
|
3795
3951
|
function isPathEqualOrWithin(parentPath, childPath) {
|
|
3796
3952
|
const parent = normalizeRepoPathForComparison(parentPath);
|
|
@@ -4338,16 +4494,25 @@ async function autoStartRuntimeServices(opts) {
|
|
|
4338
4494
|
bundlePath: sandboxPaths.remotebuddyFallbackBundlePath
|
|
4339
4495
|
} : null;
|
|
4340
4496
|
let remoteBuddyFallbackActivated = false;
|
|
4341
|
-
const maybeActivateRemoteBuddyWindowsFallback = () => {
|
|
4497
|
+
const maybeActivateRemoteBuddyWindowsFallback = (reason = "crash") => {
|
|
4342
4498
|
if (remoteBuddyFallbackActivated || !remoteBuddySourceFallback)
|
|
4343
4499
|
return false;
|
|
4344
4500
|
const tail = readLogTail(serviceLogPaths.remotebuddy, 120);
|
|
4345
|
-
if (!hasStandaloneBunCrashSignature(tail))
|
|
4501
|
+
if (reason === "crash" && !hasStandaloneBunCrashSignature(tail))
|
|
4346
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
|
+
})) {
|
|
4509
|
+
return false;
|
|
4510
|
+
}
|
|
4347
4511
|
remoteBuddyFallbackActivated = true;
|
|
4348
4512
|
lastReportedRemoteBuddyAutonomyState = "unknown";
|
|
4349
|
-
|
|
4350
|
-
|
|
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.`);
|
|
4351
4516
|
replaceService("remotebuddy", [
|
|
4352
4517
|
remoteBuddySourceFallback.bunBin,
|
|
4353
4518
|
remoteBuddySourceFallback.bundlePath,
|
|
@@ -4459,31 +4624,35 @@ ${tail}` : ""}`);
|
|
|
4459
4624
|
recordStartupPhase("workerpal", Date.now(), "disabled");
|
|
4460
4625
|
}
|
|
4461
4626
|
const scmHealthy = await probeSourceControlManager(opts.sourceControlManagerPort);
|
|
4462
|
-
const scmGitProbe = await resolveSourceControlManagerGitProbe(opts.repoRoot, runtimeEnv, process.platform);
|
|
4463
|
-
const scmRemoteStatus = await checkGitRemoteConfigured(opts.repoRoot, opts.sourceControlManagerRemote, runtimeEnv);
|
|
4464
4627
|
if (!scmHealthy) {
|
|
4465
4628
|
const scmPhaseStartedAt = Date.now();
|
|
4629
|
+
console.log("[pushpals] Checking embedded SourceControlManager git/remote preflight...");
|
|
4630
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] checking embedded source_control_manager git/remote preflight.");
|
|
4631
|
+
const scmGitProbe = await resolveSourceControlManagerGitProbe(opts.repoRoot, runtimeEnv, process.platform);
|
|
4466
4632
|
if (!scmGitProbe.ok) {
|
|
4467
4633
|
console.warn("[pushpals] Git is not available to embedded SourceControlManager; skipping SCM startup.");
|
|
4468
4634
|
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] source_control_manager skipped: git is unavailable in embedded runtime env (${scmGitProbe.detail}).`);
|
|
4469
4635
|
recordStartupPhase("source_control_manager", scmPhaseStartedAt, "skipped_no_git");
|
|
4470
|
-
} else if (scmRemoteStatus.status === "error") {
|
|
4471
|
-
console.warn(`[pushpals] Could not inspect SourceControlManager git remote "${opts.sourceControlManagerRemote}"; skipping SCM startup.`);
|
|
4472
|
-
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] source_control_manager skipped: remote "${opts.sourceControlManagerRemote}" could not be inspected (${scmRemoteStatus.detail}).`);
|
|
4473
|
-
recordStartupPhase("source_control_manager", scmPhaseStartedAt, "skipped_remote_error");
|
|
4474
|
-
} else if (scmRemoteStatus.status === "ok") {
|
|
4475
|
-
console.log(`[pushpals] Embedded SourceControlManager git=${scmGitProbe.detail}`);
|
|
4476
|
-
console.log("[pushpals] Starting embedded SourceControlManager...");
|
|
4477
|
-
const sourceControlManagerService = launchService("source_control_manager", [
|
|
4478
|
-
runtimeBinaries.sourceControlManager,
|
|
4479
|
-
"--skip-clean-check"
|
|
4480
|
-
]);
|
|
4481
|
-
console.log(`[pushpals] source_control_manager log: ${sourceControlManagerService.logPath ?? serviceLogPaths.source_control_manager}`);
|
|
4482
|
-
recordStartupPhase("source_control_manager", scmPhaseStartedAt, "started");
|
|
4483
4636
|
} else {
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4637
|
+
const scmRemoteStatus = await checkGitRemoteConfigured(opts.repoRoot, opts.sourceControlManagerRemote, runtimeEnv);
|
|
4638
|
+
if (scmRemoteStatus.status === "error") {
|
|
4639
|
+
console.warn(`[pushpals] Could not inspect SourceControlManager git remote "${opts.sourceControlManagerRemote}"; skipping SCM startup.`);
|
|
4640
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] source_control_manager skipped: remote "${opts.sourceControlManagerRemote}" could not be inspected (${scmRemoteStatus.detail}).`);
|
|
4641
|
+
recordStartupPhase("source_control_manager", scmPhaseStartedAt, "skipped_remote_error");
|
|
4642
|
+
} else if (scmRemoteStatus.status === "ok") {
|
|
4643
|
+
console.log(`[pushpals] Embedded SourceControlManager git=${scmGitProbe.detail}`);
|
|
4644
|
+
console.log("[pushpals] Starting embedded SourceControlManager...");
|
|
4645
|
+
const sourceControlManagerService = launchService("source_control_manager", [
|
|
4646
|
+
runtimeBinaries.sourceControlManager,
|
|
4647
|
+
"--skip-clean-check"
|
|
4648
|
+
]);
|
|
4649
|
+
console.log(`[pushpals] source_control_manager log: ${sourceControlManagerService.logPath ?? serviceLogPaths.source_control_manager}`);
|
|
4650
|
+
recordStartupPhase("source_control_manager", scmPhaseStartedAt, "started");
|
|
4651
|
+
} else {
|
|
4652
|
+
console.log(`[pushpals] Repo has no git remote "${opts.sourceControlManagerRemote}"; skipping embedded SourceControlManager.`);
|
|
4653
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] source_control_manager skipped: repo has no remote "${opts.sourceControlManagerRemote}".`);
|
|
4654
|
+
recordStartupPhase("source_control_manager", scmPhaseStartedAt, "skipped_no_remote");
|
|
4655
|
+
}
|
|
4487
4656
|
}
|
|
4488
4657
|
} else {
|
|
4489
4658
|
recordStartupPhase("source_control_manager", Date.now(), "reused");
|
|
@@ -4495,6 +4664,10 @@ ${tail}` : ""}`);
|
|
|
4495
4664
|
const optionalServiceExitWarned = new Set;
|
|
4496
4665
|
while (Date.now() < deadline) {
|
|
4497
4666
|
reportRemoteBuddyAutonomousEngineState();
|
|
4667
|
+
if (maybeActivateRemoteBuddyWindowsFallback("silent_startup")) {
|
|
4668
|
+
await Bun.sleep(DEFAULT_RUNTIME_BOOT_POLL_MS);
|
|
4669
|
+
continue;
|
|
4670
|
+
}
|
|
4498
4671
|
for (const service of serviceManager.getServices()) {
|
|
4499
4672
|
if (service.exited) {
|
|
4500
4673
|
if (service.name === "remotebuddy" && maybeActivateRemoteBuddyWindowsFallback()) {
|
|
@@ -5249,8 +5422,7 @@ async function main() {
|
|
|
5249
5422
|
sessionId
|
|
5250
5423
|
});
|
|
5251
5424
|
if (scmGitPrecheck.status === "failed") {
|
|
5252
|
-
console.
|
|
5253
|
-
process.exit(1);
|
|
5425
|
+
console.warn(`[pushpals] Embedded SourceControlManager precheck failed (${scmGitPrecheck.detail}); continuing startup without blocking on SCM.`);
|
|
5254
5426
|
}
|
|
5255
5427
|
workerpalDockerPrecheck = await precheckWorkerpalDockerAvailability({
|
|
5256
5428
|
repoRoot,
|
|
@@ -5460,12 +5632,16 @@ async function main() {
|
|
|
5460
5632
|
}
|
|
5461
5633
|
process.exit(1);
|
|
5462
5634
|
}
|
|
5463
|
-
const
|
|
5635
|
+
const shouldProbeWorkerpalStartupCapacity = Boolean(config.remotebuddy.autoSpawnWorkerpals);
|
|
5636
|
+
const workerpalCapacity = shouldProbeWorkerpalStartupCapacity ? await waitForWorkerpalCapacity({
|
|
5464
5637
|
serverUrl,
|
|
5465
5638
|
timeoutMs: resolveWorkerpalStartupReadinessProbeTimeoutMs(config),
|
|
5466
5639
|
ttlMs: config.remotebuddy.workerpalOnlineTtlMs
|
|
5467
|
-
})
|
|
5468
|
-
|
|
5640
|
+
}) : {
|
|
5641
|
+
ok: false,
|
|
5642
|
+
detail: "WorkerPal auto-spawn is disabled"
|
|
5643
|
+
};
|
|
5644
|
+
if (shouldProbeWorkerpalStartupCapacity && !workerpalCapacity.ok) {
|
|
5469
5645
|
console.warn(`[pushpals] WorkerPal readiness probe did not find idle capacity yet (${workerpalCapacity.detail}).`);
|
|
5470
5646
|
console.warn("[pushpals] Continuing startup; WorkerPal warmup may still be in progress and first task dispatch can be delayed.");
|
|
5471
5647
|
if (workerpalDockerPrecheck.status === "failed") {
|
|
@@ -5478,7 +5654,13 @@ async function main() {
|
|
|
5478
5654
|
const startupWorkerExecutionReadiness = workerpalCapacity.ok ? {
|
|
5479
5655
|
state: "ready",
|
|
5480
5656
|
detail: workerpalCapacity.detail
|
|
5481
|
-
} :
|
|
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({
|
|
5482
5664
|
autoSpawnWorkerpals: Boolean(config.remotebuddy.autoSpawnWorkerpals),
|
|
5483
5665
|
requireDocker: Boolean(config.remotebuddy.workerpalDocker) && Boolean(config.remotebuddy.workerpalRequireDocker),
|
|
5484
5666
|
dockerPrecheck: workerpalDockerPrecheck,
|
|
@@ -5689,8 +5871,10 @@ export {
|
|
|
5689
5871
|
waitForWorkerpalCapacity,
|
|
5690
5872
|
startEmbeddedMonitoringHub,
|
|
5691
5873
|
shutdownEmbeddedServiceManagerGracefully,
|
|
5874
|
+
shouldUseRemoteBuddySilentStartupFallback,
|
|
5692
5875
|
shouldRunEmbeddedRuntimeStartupPrechecks,
|
|
5693
5876
|
shouldRestartEmbeddedService,
|
|
5877
|
+
runCommandWithEnv,
|
|
5694
5878
|
resolveWorkerpalDockerProbe,
|
|
5695
5879
|
resolveWorkerExecutionReadiness,
|
|
5696
5880
|
resolveWindowsWhereExecutableCandidatesForEnv,
|
|
@@ -5716,6 +5900,7 @@ export {
|
|
|
5716
5900
|
isDockerCleanupTimeoutDetail,
|
|
5717
5901
|
isCliExitCommand,
|
|
5718
5902
|
injectMonitoringHubBootstrap,
|
|
5903
|
+
hasRemoteBuddyRuntimeOutput,
|
|
5719
5904
|
formatWorkerExecutionReadinessLines,
|
|
5720
5905
|
formatTimestampedCliLine,
|
|
5721
5906
|
formatSessionEventLine,
|