@pushpalsdev/cli 1.1.28 → 1.1.30
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/bin/pushpals.cjs +146 -21
- package/dist/pushpals-cli.js +31 -0
- package/package.json +1 -1
- package/runtime/sandbox/.pushpals-remotebuddy-fallback.js +17 -0
- package/runtime/sandbox/apps/workerpals/src/backends/miniswe_backend.ts +1 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex_backend.ts +1 -0
- package/runtime/sandbox/apps/workerpals/src/common/generic_python_executor.ts +30 -4
package/bin/pushpals.cjs
CHANGED
|
@@ -2,13 +2,20 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
const { spawn, spawnSync } = require("node:child_process");
|
|
5
|
-
const { existsSync, readFileSync } = require("node:fs");
|
|
6
|
-
const {
|
|
5
|
+
const { existsSync, mkdirSync, readFileSync, rmSync } = require("node:fs");
|
|
6
|
+
const { tmpdir } = require("node:os");
|
|
7
|
+
const { join, resolve } = require("node:path");
|
|
7
8
|
|
|
8
9
|
const bundledCliPath = resolve(__dirname, "..", "dist", "pushpals-cli.js");
|
|
9
10
|
const packageJsonPath = resolve(__dirname, "..", "package.json");
|
|
10
11
|
const releaseUrl = "https://github.com/PushPalsDev/pushpals/releases";
|
|
12
|
+
const DEFAULT_BUN_PROBE_TIMEOUT_MS = 10_000;
|
|
13
|
+
const DEFAULT_BOOTSTRAP_TIMEOUT_MS = 5 * 60 * 1000;
|
|
14
|
+
const BUN_PROBE_TIMEOUT_ENV = "PUSHPALS_BUN_PROBE_TIMEOUT_MS";
|
|
15
|
+
const BOOTSTRAP_TIMEOUT_ENV = "PUSHPALS_CLI_BOOTSTRAP_TIMEOUT_MS";
|
|
16
|
+
const BOOTSTRAP_READY_MARKER_ENV = "PUSHPALS_CLI_READY_MARKER";
|
|
11
17
|
let packageVersion = "";
|
|
18
|
+
let readyMarkerPath = "";
|
|
12
19
|
if (existsSync(packageJsonPath)) {
|
|
13
20
|
try {
|
|
14
21
|
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
@@ -25,6 +32,48 @@ function fail(lines) {
|
|
|
25
32
|
process.exit(1);
|
|
26
33
|
}
|
|
27
34
|
|
|
35
|
+
function parseBoundedTimeoutMs(envName, defaultValue, maxValue) {
|
|
36
|
+
const raw = String(process.env[envName] ?? "").trim();
|
|
37
|
+
if (raw === "0") return 0;
|
|
38
|
+
const parsed = Number.parseInt(raw, 10);
|
|
39
|
+
if (!Number.isFinite(parsed) || parsed < 0) return defaultValue;
|
|
40
|
+
return Math.max(1_000, Math.min(maxValue, parsed));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function parseBunProbeTimeoutMs() {
|
|
44
|
+
return parseBoundedTimeoutMs(BUN_PROBE_TIMEOUT_ENV, DEFAULT_BUN_PROBE_TIMEOUT_MS, 60 * 1000);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parseBootstrapTimeoutMs() {
|
|
48
|
+
return parseBoundedTimeoutMs(
|
|
49
|
+
BOOTSTRAP_TIMEOUT_ENV,
|
|
50
|
+
DEFAULT_BOOTSTRAP_TIMEOUT_MS,
|
|
51
|
+
30 * 60 * 1000,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function createReadyMarkerPath() {
|
|
56
|
+
const root = join(tmpdir(), "pushpals-cli-ready");
|
|
57
|
+
mkdirSync(root, { recursive: true });
|
|
58
|
+
return join(root, `ready-${process.pid}-${Date.now()}.txt`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function killChildTree(child) {
|
|
62
|
+
if (!child || child.exitCode !== null || child.signalCode !== null) return;
|
|
63
|
+
try {
|
|
64
|
+
if (process.platform === "win32" && typeof child.pid === "number" && child.pid > 0) {
|
|
65
|
+
spawnSync("taskkill", ["/PID", String(child.pid), "/T", "/F"], {
|
|
66
|
+
stdio: "ignore",
|
|
67
|
+
timeout: 10_000,
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
child.kill("SIGKILL");
|
|
72
|
+
} catch {
|
|
73
|
+
// best-effort watchdog cleanup only
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
28
77
|
if (!existsSync(bundledCliPath)) {
|
|
29
78
|
fail([
|
|
30
79
|
"[pushpals] CLI bundle is missing in this package install.",
|
|
@@ -33,16 +82,26 @@ if (!existsSync(bundledCliPath)) {
|
|
|
33
82
|
]);
|
|
34
83
|
}
|
|
35
84
|
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
85
|
+
function probeBunRuntime() {
|
|
86
|
+
const timeout = parseBunProbeTimeoutMs();
|
|
87
|
+
const options = { stdio: "ignore", timeout };
|
|
88
|
+
const result = spawnSync("bun", ["--version"], options);
|
|
89
|
+
return {
|
|
90
|
+
ok: result.status === 0,
|
|
91
|
+
timedOut: Boolean(result.error && result.error.code === "ETIMEDOUT"),
|
|
92
|
+
};
|
|
43
93
|
}
|
|
44
94
|
|
|
45
|
-
|
|
95
|
+
const bunRuntime = probeBunRuntime();
|
|
96
|
+
if (!bunRuntime.ok) {
|
|
97
|
+
if (bunRuntime.timedOut) {
|
|
98
|
+
fail([
|
|
99
|
+
`[pushpals] Bun runtime probe timed out after ${parseBunProbeTimeoutMs()}ms.`,
|
|
100
|
+
"[pushpals] This usually means the Bun process wedged during startup; the CLI refused to continue so it does not freeze the shell.",
|
|
101
|
+
`[pushpals] Set ${BUN_PROBE_TIMEOUT_ENV}=0 to disable this probe timeout, or use a direct binary release:`,
|
|
102
|
+
`[pushpals] ${releaseUrl}`,
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
46
105
|
fail([
|
|
47
106
|
"[pushpals] Bun runtime is required for the npm package entrypoint.",
|
|
48
107
|
"[pushpals] Install Bun from https://bun.sh, or use a direct binary release:",
|
|
@@ -51,38 +110,104 @@ if (!hasBunRuntime()) {
|
|
|
51
110
|
}
|
|
52
111
|
|
|
53
112
|
function spawnBunCli() {
|
|
113
|
+
readyMarkerPath = process.env[BOOTSTRAP_READY_MARKER_ENV] || createReadyMarkerPath();
|
|
54
114
|
const childEnv = {
|
|
55
115
|
...process.env,
|
|
56
116
|
PUSHPALS_CLI_PACKAGE_VERSION: packageVersion || process.env.PUSHPALS_CLI_PACKAGE_VERSION || "",
|
|
117
|
+
[BOOTSTRAP_READY_MARKER_ENV]: readyMarkerPath,
|
|
57
118
|
};
|
|
58
119
|
|
|
59
|
-
if (process.platform
|
|
60
|
-
|
|
120
|
+
if (process.platform === "win32") {
|
|
121
|
+
const quoteWindows = (value) => `"${String(value).replace(/"/g, '\\"')}"`;
|
|
122
|
+
const commandLine = [
|
|
123
|
+
"bun",
|
|
124
|
+
quoteWindows(bundledCliPath),
|
|
125
|
+
...process.argv.slice(2).map(quoteWindows),
|
|
126
|
+
].join(" ");
|
|
127
|
+
return spawn(commandLine, {
|
|
128
|
+
shell: true,
|
|
61
129
|
stdio: "inherit",
|
|
62
130
|
env: childEnv,
|
|
63
131
|
});
|
|
64
132
|
}
|
|
65
|
-
|
|
66
|
-
const quoteWindows = (value) => `"${String(value).replace(/"/g, '\\"')}"`;
|
|
67
|
-
const commandLine = [
|
|
68
|
-
"bun",
|
|
69
|
-
quoteWindows(bundledCliPath),
|
|
70
|
-
...process.argv.slice(2).map(quoteWindows),
|
|
71
|
-
].join(" ");
|
|
72
|
-
return spawn(commandLine, {
|
|
73
|
-
shell: true,
|
|
133
|
+
return spawn("bun", [bundledCliPath, ...process.argv.slice(2)], {
|
|
74
134
|
stdio: "inherit",
|
|
75
135
|
env: childEnv,
|
|
76
136
|
});
|
|
77
137
|
}
|
|
78
138
|
|
|
79
139
|
const child = spawnBunCli();
|
|
140
|
+
const bootstrapTimeoutMs = parseBootstrapTimeoutMs();
|
|
141
|
+
let watchdogTimer = null;
|
|
142
|
+
let markerPollTimer = null;
|
|
143
|
+
let watchdogFired = false;
|
|
144
|
+
let parentSignalExit = false;
|
|
145
|
+
|
|
146
|
+
function cleanupReadyMarker() {
|
|
147
|
+
if (!readyMarkerPath) return;
|
|
148
|
+
try {
|
|
149
|
+
rmSync(readyMarkerPath, { force: true });
|
|
150
|
+
} catch {
|
|
151
|
+
// best-effort cleanup only
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function clearBootstrapWatchdog() {
|
|
156
|
+
if (watchdogTimer) {
|
|
157
|
+
clearTimeout(watchdogTimer);
|
|
158
|
+
watchdogTimer = null;
|
|
159
|
+
}
|
|
160
|
+
if (markerPollTimer) {
|
|
161
|
+
clearInterval(markerPollTimer);
|
|
162
|
+
markerPollTimer = null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (bootstrapTimeoutMs > 0) {
|
|
167
|
+
markerPollTimer = setInterval(() => {
|
|
168
|
+
if (readyMarkerPath && existsSync(readyMarkerPath)) {
|
|
169
|
+
clearBootstrapWatchdog();
|
|
170
|
+
}
|
|
171
|
+
}, 1_000);
|
|
172
|
+
watchdogTimer = setTimeout(() => {
|
|
173
|
+
if (readyMarkerPath && existsSync(readyMarkerPath)) {
|
|
174
|
+
clearBootstrapWatchdog();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
watchdogFired = true;
|
|
178
|
+
process.stderr.write(
|
|
179
|
+
`[pushpals] Bun runtime did not finish CLI bootstrap within ${bootstrapTimeoutMs}ms; terminating Bun process tree. ` +
|
|
180
|
+
`Set ${BOOTSTRAP_TIMEOUT_ENV}=0 to disable this watchdog.\n`,
|
|
181
|
+
);
|
|
182
|
+
killChildTree(child);
|
|
183
|
+
}, bootstrapTimeoutMs);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function terminateChildAndExit(signal) {
|
|
187
|
+
if (parentSignalExit) return;
|
|
188
|
+
parentSignalExit = true;
|
|
189
|
+
clearBootstrapWatchdog();
|
|
190
|
+
killChildTree(child);
|
|
191
|
+
cleanupReadyMarker();
|
|
192
|
+
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
process.once("SIGINT", () => terminateChildAndExit("SIGINT"));
|
|
196
|
+
process.once("SIGTERM", () => terminateChildAndExit("SIGTERM"));
|
|
80
197
|
|
|
81
198
|
child.on("error", (err) => {
|
|
199
|
+
clearBootstrapWatchdog();
|
|
200
|
+
cleanupReadyMarker();
|
|
82
201
|
fail([`[pushpals] Failed to launch Bun runtime: ${String(err?.message ?? err)}`]);
|
|
83
202
|
});
|
|
84
203
|
|
|
85
204
|
child.on("exit", (code, signal) => {
|
|
205
|
+
clearBootstrapWatchdog();
|
|
206
|
+
cleanupReadyMarker();
|
|
207
|
+
if (watchdogFired) {
|
|
208
|
+
process.exit(124);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
86
211
|
if (signal) {
|
|
87
212
|
process.kill(process.pid, signal);
|
|
88
213
|
return;
|
package/dist/pushpals-cli.js
CHANGED
|
@@ -1652,6 +1652,8 @@ var EMBEDDED_SERVICE_RESTART_MAX_ATTEMPTS = 4;
|
|
|
1652
1652
|
var DEFAULT_WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS = 5000;
|
|
1653
1653
|
var WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS_ENV = "PUSHPALS_WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS";
|
|
1654
1654
|
var BLOCKING_WORKERPAL_IMAGE_BUILD_ENV = "PUSHPALS_BLOCKING_WORKERPAL_IMAGE_BUILD";
|
|
1655
|
+
var WINDOWS_FRESH_RUNTIME_WORKERPAL_PREWARM_DELAY_MS_ENV = "PUSHPALS_WINDOWS_FRESH_RUNTIME_WORKERPAL_PREWARM_DELAY_MS";
|
|
1656
|
+
var DEFAULT_WINDOWS_FRESH_RUNTIME_WORKERPAL_PREWARM_DELAY_MS = 30000;
|
|
1655
1657
|
var CLI_SESSION_JOB_LOG_MAX_CHARS = 700;
|
|
1656
1658
|
var CLI_SESSION_SHOW_JOB_EVENTS_ENV = "PUSHPALS_CLI_SHOW_JOB_EVENTS";
|
|
1657
1659
|
var EMBEDDED_RUNTIME_SAFETY_CAP_DISABLE_ENV = "PUSHPALS_DISABLE_EMBEDDED_SAFETY_CAPS";
|
|
@@ -3112,6 +3114,7 @@ async function ensureRuntimeBinaries(runtimeRoot, runtimeTag) {
|
|
|
3112
3114
|
console.log(`[pushpals] Embedded runtime binaries downloaded: ${downloadedCount}.`);
|
|
3113
3115
|
}
|
|
3114
3116
|
console.log("[pushpals] Embedded runtime binaries are ready.");
|
|
3117
|
+
runtimeBinaries.freshlyInstalled = downloadedCount > 0;
|
|
3115
3118
|
return runtimeBinaries;
|
|
3116
3119
|
}
|
|
3117
3120
|
function buildServiceStopCommand(pid, platform = process.platform) {
|
|
@@ -3915,6 +3918,14 @@ function resolveWorkerpalStartupReadinessProbeMaxMs(env = process.env) {
|
|
|
3915
3918
|
function resolveWorkerpalStartupReadinessProbeTimeoutMs(config) {
|
|
3916
3919
|
return Math.max(1000, Math.min(resolveWorkerpalCapacityTimeoutMs(config), resolveWorkerpalStartupReadinessProbeMaxMs()));
|
|
3917
3920
|
}
|
|
3921
|
+
function resolveWindowsFreshRuntimeWorkerpalPrewarmDelayMs(env = process.env, platform = process.platform) {
|
|
3922
|
+
if (platform !== "win32")
|
|
3923
|
+
return 0;
|
|
3924
|
+
const raw = String(env[WINDOWS_FRESH_RUNTIME_WORKERPAL_PREWARM_DELAY_MS_ENV] ?? "").trim();
|
|
3925
|
+
if (raw === "0")
|
|
3926
|
+
return 0;
|
|
3927
|
+
return clampPositiveInt(parsePositiveInt(raw, DEFAULT_WINDOWS_FRESH_RUNTIME_WORKERPAL_PREWARM_DELAY_MS), 0, 5 * 60000);
|
|
3928
|
+
}
|
|
3918
3929
|
function shouldPrepareEmbeddedWorkerpalDockerImageBlocking(opts = {}) {
|
|
3919
3930
|
const env = opts.env ?? process.env;
|
|
3920
3931
|
const explicit = String(env[BLOCKING_WORKERPAL_IMAGE_BUILD_ENV] ?? "").trim();
|
|
@@ -4457,6 +4468,13 @@ async function autoStartRuntimeServices(opts) {
|
|
|
4457
4468
|
runtimeTag
|
|
4458
4469
|
});
|
|
4459
4470
|
runtimeEnv.PUSHPALS_WORKERPALS_BIN = runtimeBinaries.workerpals;
|
|
4471
|
+
if (runtimeBinaries.freshlyInstalled && process.platform === "win32" && runtimePreflight.config.remotebuddy.autoSpawnWorkerpals && !runtimeEnv.PUSHPALS_REMOTEBUDDY_WORKERPAL_PREWARM_DELAY_MS) {
|
|
4472
|
+
const delayMs = resolveWindowsFreshRuntimeWorkerpalPrewarmDelayMs(runtimeEnv, process.platform);
|
|
4473
|
+
if (delayMs > 0) {
|
|
4474
|
+
runtimeEnv.PUSHPALS_REMOTEBUDDY_WORKERPAL_PREWARM_DELAY_MS = String(delayMs);
|
|
4475
|
+
console.log(`[pushpals] Fresh Windows runtime binaries detected; delaying WorkerPal prewarm by ${delayMs}ms so security software can finish first-run binary checks. Set ${WINDOWS_FRESH_RUNTIME_WORKERPAL_PREWARM_DELAY_MS_ENV}=0 to disable.`);
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4460
4478
|
const preconfiguredRuntimeGitBinary = runtimeEnv.PUSHPALS_GIT_BIN_ABSOLUTE ?? runtimeEnv.PUSHPALS_GIT_BIN;
|
|
4461
4479
|
if (preconfiguredRuntimeGitBinary) {
|
|
4462
4480
|
applyResolvedGitBinaryToRuntimeEnv(runtimeEnv, preconfiguredRuntimeGitBinary);
|
|
@@ -4939,6 +4957,17 @@ function writeCliState(pathValue, state) {
|
|
|
4939
4957
|
writeFileSync(pathValue, `${JSON.stringify(payload, null, 2)}
|
|
4940
4958
|
`, "utf8");
|
|
4941
4959
|
}
|
|
4960
|
+
function markCliBootstrapReadyFromEnv(env = process.env) {
|
|
4961
|
+
const markerPath = String(env.PUSHPALS_CLI_READY_MARKER ?? "").trim();
|
|
4962
|
+
if (!markerPath)
|
|
4963
|
+
return;
|
|
4964
|
+
try {
|
|
4965
|
+
mkdirSync(dirname(markerPath), { recursive: true });
|
|
4966
|
+
writeFileSync(markerPath, `${process.pid}
|
|
4967
|
+
${new Date().toISOString()}
|
|
4968
|
+
`, "utf8");
|
|
4969
|
+
} catch {}
|
|
4970
|
+
}
|
|
4942
4971
|
function resolveCliStatePath(repoRoot) {
|
|
4943
4972
|
return resolveGitStateFilePath(repoRoot, "pushpals-cli-state.json");
|
|
4944
4973
|
}
|
|
@@ -5950,6 +5979,7 @@ async function main() {
|
|
|
5950
5979
|
console.log(`[pushpals] cliStateFile=${statePath ?? "unavailable"}`);
|
|
5951
5980
|
reportWorkerExecutionReadinessFromSnapshot(startupWorkerExecutionReadiness);
|
|
5952
5981
|
reportEmbeddedRuntimeHealth();
|
|
5982
|
+
markCliBootstrapReadyFromEnv();
|
|
5953
5983
|
if (parsed.runtimeOnly) {
|
|
5954
5984
|
console.log("[pushpals] runtimeOnly=true");
|
|
5955
5985
|
} else {
|
|
@@ -6124,6 +6154,7 @@ export {
|
|
|
6124
6154
|
resolveWorkerExecutionReadiness,
|
|
6125
6155
|
resolveWindowsWhereExecutableCandidatesForEnv,
|
|
6126
6156
|
resolveWindowsShellExecutableCandidatesForEnv,
|
|
6157
|
+
resolveWindowsFreshRuntimeWorkerpalPrewarmDelayMs,
|
|
6127
6158
|
resolveRuntimeGitExecutableCandidates,
|
|
6128
6159
|
resolveRuntimeDockerExecutableCandidates,
|
|
6129
6160
|
resolvePreferredRuntimeReleaseTag,
|
package/package.json
CHANGED
|
@@ -8396,6 +8396,12 @@ function parseEnabledFlag(raw, defaultValue) {
|
|
|
8396
8396
|
return defaultValue;
|
|
8397
8397
|
return !["0", "false", "no", "off"].includes(text);
|
|
8398
8398
|
}
|
|
8399
|
+
function parseNonNegativeMs(raw, defaultValue = 0) {
|
|
8400
|
+
const parsed = Number.parseInt(String(raw ?? "").trim(), 10);
|
|
8401
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
8402
|
+
return Math.max(0, defaultValue);
|
|
8403
|
+
return Math.floor(parsed);
|
|
8404
|
+
}
|
|
8399
8405
|
function isCodexUnavailableFailureSignal(message, detail) {
|
|
8400
8406
|
const text = `${message}
|
|
8401
8407
|
${detail}`.toLowerCase();
|
|
@@ -8968,6 +8974,7 @@ class RemoteBuddyOrchestrator {
|
|
|
8968
8974
|
workerSpawnCooldownUntil = 0;
|
|
8969
8975
|
workerSpawnBackoffMs;
|
|
8970
8976
|
workerAutoscalePollMs;
|
|
8977
|
+
workerPrewarmDelayMs;
|
|
8971
8978
|
lastWorkerAutoscaleAt = 0;
|
|
8972
8979
|
comm;
|
|
8973
8980
|
statusHeartbeatTimer = null;
|
|
@@ -9030,6 +9037,7 @@ class RemoteBuddyOrchestrator {
|
|
|
9030
9037
|
this.workerpalsUnavailableReason = null;
|
|
9031
9038
|
this.workerSpawnBackoffMs = Math.max(1000, Number.isFinite(remoteCfg.crashRestartBackoffMs) && remoteCfg.crashRestartBackoffMs > 0 ? remoteCfg.crashRestartBackoffMs : 3000);
|
|
9032
9039
|
this.workerAutoscalePollMs = Math.max(1000, remoteCfg.pollMs);
|
|
9040
|
+
this.workerPrewarmDelayMs = Math.min(5 * 60000, parseNonNegativeMs(process.env.PUSHPALS_REMOTEBUDDY_WORKERPAL_PREWARM_DELAY_MS, 0));
|
|
9033
9041
|
this.statusHeartbeatMs = Math.max(0, remoteCfg.statusHeartbeatMs);
|
|
9034
9042
|
this.fetchFailureLogsOnJobFailure = parseEnabledFlag(process.env.REMOTEBUDDY_FETCH_FAILURE_LOGS, true);
|
|
9035
9043
|
this.executionBudgetInteractiveMs = Math.max(60000, remoteCfg.executionBudgetInteractiveMs);
|
|
@@ -9081,6 +9089,9 @@ class RemoteBuddyOrchestrator {
|
|
|
9081
9089
|
this.autonomousEngine.setRuntimeEnabled(this.autonomyRuntimeEnabled);
|
|
9082
9090
|
console.log(`[RemoteBuddy] Detected repo root: ${this.repo}`);
|
|
9083
9091
|
console.log(`[RemoteBuddy] Worker scheduler: min=${this.minWorkers} max=${this.maxWorkers} autoSpawn=${this.autoSpawnWorkers ? "on" : "off"} wait=${this.waitForWorkerMs}ms`);
|
|
9092
|
+
if (this.workerPrewarmDelayMs > 0) {
|
|
9093
|
+
console.log(`[RemoteBuddy] WorkerPal startup prewarm delayed by ${this.workerPrewarmDelayMs}ms to reduce first-run binary scan contention.`);
|
|
9094
|
+
}
|
|
9084
9095
|
console.log(`[RemoteBuddy] Budgets: interactive=${this.executionBudgetInteractiveMs}ms normal=${this.executionBudgetNormalMs}ms background=${this.executionBudgetBackgroundMs}ms finalization=${this.finalizationBudgetMs}ms`);
|
|
9085
9096
|
console.log(`[RemoteBuddy] Failure log fetch on job failures: ${this.fetchFailureLogsOnJobFailure ? "on" : "off"}`);
|
|
9086
9097
|
console.log(`[RemoteBuddy] Persistent memory: ${this.memoryEnabled ? "on" : "off"} crossSession=${this.memoryIncludeCrossSession ? "on" : "off"} recallItems=${this.memoryMaxRecallItems} recallChars=${this.memoryMaxRecallChars} retentionDays=${this.memoryRetentionDays}`);
|
|
@@ -10039,6 +10050,12 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
10039
10050
|
}
|
|
10040
10051
|
}
|
|
10041
10052
|
async ensureWorkerCapacityOnStartup() {
|
|
10053
|
+
if (this.workerPrewarmDelayMs > 0) {
|
|
10054
|
+
console.log(`[RemoteBuddy] Waiting ${this.workerPrewarmDelayMs}ms before WorkerPal startup prewarm.`);
|
|
10055
|
+
await Bun.sleep(this.workerPrewarmDelayMs);
|
|
10056
|
+
if (this.disposed)
|
|
10057
|
+
return;
|
|
10058
|
+
}
|
|
10042
10059
|
const workers = await this.fetchWorkers();
|
|
10043
10060
|
if (this.pickIdleWorker(workers)) {
|
|
10044
10061
|
return;
|
|
@@ -42,6 +42,7 @@ export const MINISWE_BACKEND: DockerBackendSpec = {
|
|
|
42
42
|
taskExecute: createGenericPythonExecutor({
|
|
43
43
|
backendName: "miniswe",
|
|
44
44
|
scriptPath: resolve(import.meta.dir, "miniswe", "miniswe_executor.py"),
|
|
45
|
+
scriptSegments: ["apps", "workerpals", "src", "backends", "miniswe", "miniswe_executor.py"],
|
|
45
46
|
pythonConfigKey: "miniswePython",
|
|
46
47
|
timeoutConfigKey: "minisweTimeoutMs",
|
|
47
48
|
}),
|
|
@@ -61,6 +61,7 @@ export const OPENAI_CODEX_BACKEND: DockerBackendSpec = {
|
|
|
61
61
|
taskExecute: createGenericPythonExecutor({
|
|
62
62
|
backendName: "openai_codex",
|
|
63
63
|
scriptPath: resolve(import.meta.dir, "openai_codex", "openai_codex_executor.py"),
|
|
64
|
+
scriptSegments: ["apps", "workerpals", "src", "backends", "openai_codex", "openai_codex_executor.py"],
|
|
64
65
|
pythonConfigKey: "openaiCodexPython",
|
|
65
66
|
timeoutConfigKey: "openaiCodexTimeoutMs",
|
|
66
67
|
}),
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { existsSync } from "fs";
|
|
11
|
-
import { resolve } from "path";
|
|
11
|
+
import { dirname, join, resolve } from "path";
|
|
12
12
|
import type { JobResult, JobTokenUsage } from "./types.js";
|
|
13
13
|
import type { WorkerpalsRuntimeConfig } from "./executor_backend.js";
|
|
14
14
|
import type { BackendTaskExecutor } from "../backends/types.js";
|
|
@@ -23,6 +23,7 @@ import { buildWorkerSandboxWritableEnv } from "./sandbox_env.js";
|
|
|
23
23
|
interface GenericPythonExecutorConfig {
|
|
24
24
|
backendName: string;
|
|
25
25
|
scriptPath: string;
|
|
26
|
+
scriptSegments?: readonly string[];
|
|
26
27
|
pythonConfigKey: string;
|
|
27
28
|
timeoutConfigKey: string;
|
|
28
29
|
capTimeoutToExecutionBudget?: boolean;
|
|
@@ -231,6 +232,28 @@ function formatGenericPythonExecutorTimeoutDetail(
|
|
|
231
232
|
return `${configPath}=${configuredTimeoutMs}ms within planning executionBudgetMs=${executionBudgetMs}ms`;
|
|
232
233
|
}
|
|
233
234
|
|
|
235
|
+
function uniqueStrings(values: string[]): string[] {
|
|
236
|
+
return Array.from(new Set(values.filter(Boolean)));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function resolveGenericPythonExecutorScriptPath(
|
|
240
|
+
config: GenericPythonExecutorConfig,
|
|
241
|
+
runtimeConfig: WorkerpalsRuntimeConfig,
|
|
242
|
+
): { scriptPath: string | null; candidates: string[] } {
|
|
243
|
+
const candidates = [config.scriptPath];
|
|
244
|
+
if (config.scriptSegments && config.scriptSegments.length > 0) {
|
|
245
|
+
const runtimeRoot = dirname(runtimeConfig.configDir);
|
|
246
|
+
candidates.push(join(runtimeRoot, ...config.scriptSegments));
|
|
247
|
+
candidates.push(join(runtimeConfig.projectRoot, ...config.scriptSegments));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const uniqueCandidates = uniqueStrings(candidates.map((candidate) => resolve(candidate)));
|
|
251
|
+
return {
|
|
252
|
+
scriptPath: uniqueCandidates.find((candidate) => existsSync(candidate)) ?? null,
|
|
253
|
+
candidates: uniqueCandidates,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
234
257
|
export function normalizeGenericPythonExecutorParsedResultForTimeout(params: {
|
|
235
258
|
backendName: string;
|
|
236
259
|
kind: string;
|
|
@@ -278,7 +301,7 @@ export function normalizeGenericPythonExecutorParsedResultForTimeout(params: {
|
|
|
278
301
|
export function createGenericPythonExecutor(
|
|
279
302
|
config: GenericPythonExecutorConfig,
|
|
280
303
|
): BackendTaskExecutor {
|
|
281
|
-
const { backendName
|
|
304
|
+
const { backendName } = config;
|
|
282
305
|
const backendLabel = backendName[0].toUpperCase() + backendName.slice(1);
|
|
283
306
|
|
|
284
307
|
return async (
|
|
@@ -289,10 +312,13 @@ export function createGenericPythonExecutor(
|
|
|
289
312
|
onLog?: (stream: "stdout" | "stderr", line: string) => void,
|
|
290
313
|
budgets?: { executionBudgetMs?: number; finalizationBudgetMs?: number },
|
|
291
314
|
): Promise<JobResult> => {
|
|
292
|
-
|
|
315
|
+
const resolvedScript = resolveGenericPythonExecutorScriptPath(config, runtimeConfig);
|
|
316
|
+
const scriptPath = resolvedScript.scriptPath;
|
|
317
|
+
if (scriptPath == null) {
|
|
293
318
|
return {
|
|
294
319
|
ok: false,
|
|
295
|
-
summary: `${backendName} wrapper script not found
|
|
320
|
+
summary: `${backendName} wrapper script not found`,
|
|
321
|
+
stderr: `Checked wrapper script path(s): ${resolvedScript.candidates.join("; ")}`,
|
|
296
322
|
exitCode: 1,
|
|
297
323
|
};
|
|
298
324
|
}
|