@pushpalsdev/cli 1.0.24 → 1.0.27
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 +126 -7
- package/package.json +1 -1
- package/runtime/configs/default.toml +3 -3
- package/runtime/configs/local.example.toml +3 -3
- package/runtime/sandbox/configs/default.toml +3 -3
- package/runtime/sandbox/configs/local.example.toml +3 -3
- package/runtime/sandbox/packages/shared/src/config.ts +4 -1
package/dist/pushpals-cli.js
CHANGED
|
@@ -637,7 +637,7 @@ function loadPushPalsConfig(options = {}) {
|
|
|
637
637
|
workerpalOnlineTtlMs: Math.max(1000, asInt(parseIntEnv("REMOTEBUDDY_WORKERPAL_ONLINE_TTL_MS") ?? remoteNode.workerpal_online_ttl_ms, 15000)),
|
|
638
638
|
waitForWorkerpalMs: Math.max(0, asInt(parseIntEnv("REMOTEBUDDY_WAIT_FOR_WORKERPAL_MS") ?? remoteNode.wait_for_workerpal_ms, 15000)),
|
|
639
639
|
autoSpawnWorkerpals: parseBoolEnv("REMOTEBUDDY_AUTO_SPAWN_WORKERPALS") ?? asBoolean(remoteNode.auto_spawn_workerpals, true),
|
|
640
|
-
maxWorkerpals: Math.max(1, asInt(remoteNode.max_workerpals, 20)),
|
|
640
|
+
maxWorkerpals: Math.max(1, asInt(parseIntEnv("REMOTEBUDDY_MAX_WORKERPALS") ?? remoteNode.max_workerpals, 20)),
|
|
641
641
|
workerpalStartupTimeoutMs: Math.max(1000, asInt(parseIntEnv("REMOTEBUDDY_WORKERPAL_STARTUP_TIMEOUT_MS") ?? remoteNode.workerpal_startup_timeout_ms, 1e4)),
|
|
642
642
|
workerpalDocker: parseBoolEnv("REMOTEBUDDY_WORKERPAL_DOCKER") ?? asBoolean(remoteNode.workerpal_docker, true),
|
|
643
643
|
workerpalRequireDocker: parseBoolEnv("REMOTEBUDDY_WORKERPAL_REQUIRE_DOCKER") ?? asBoolean(remoteNode.workerpal_require_docker, true),
|
|
@@ -1743,6 +1743,21 @@ function writeTextFileIfMissing(pathValue, text) {
|
|
|
1743
1743
|
mkdirSync(dirname(pathValue), { recursive: true });
|
|
1744
1744
|
writeFileSync(pathValue, text, "utf8");
|
|
1745
1745
|
}
|
|
1746
|
+
function migrateEmbeddedRuntimeLocalToml(localTomlPath) {
|
|
1747
|
+
if (!existsSync4(localTomlPath))
|
|
1748
|
+
return;
|
|
1749
|
+
let original;
|
|
1750
|
+
try {
|
|
1751
|
+
original = readFileSync4(localTomlPath, "utf8");
|
|
1752
|
+
} catch {
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
const updated = original.replace(/^(\[remotebuddy\.autonomy\]\r?\n)(enabled\s*=\s*false\s*\r?\n)/m, `$1enabled = true
|
|
1756
|
+
`);
|
|
1757
|
+
if (updated !== original) {
|
|
1758
|
+
writeFileSync(localTomlPath, updated, "utf8");
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1746
1761
|
function copyRuntimeAssetBundle(source, runtimeRoot, force) {
|
|
1747
1762
|
mkdirSync(runtimeRoot, { recursive: true });
|
|
1748
1763
|
cpSync(source.envExamplePath, join2(runtimeRoot, ".env.example"), {
|
|
@@ -1782,12 +1797,14 @@ function seedRuntimePreflightAssets(runtimeRoot) {
|
|
|
1782
1797
|
writeTextFileIfMissing(join2(runtimeRoot, ".env"), `# Local PushPals runtime environment
|
|
1783
1798
|
`);
|
|
1784
1799
|
const localExamplePath = join2(runtimeRoot, "configs", "local.example.toml");
|
|
1800
|
+
const localTomlPath = join2(runtimeRoot, "configs", "local.toml");
|
|
1785
1801
|
if (existsSync4(localExamplePath)) {
|
|
1786
|
-
writeTextFileIfMissing(
|
|
1802
|
+
writeTextFileIfMissing(localTomlPath, readFileSync4(localExamplePath, "utf8"));
|
|
1787
1803
|
} else {
|
|
1788
|
-
writeTextFileIfMissing(
|
|
1804
|
+
writeTextFileIfMissing(localTomlPath, `# Local PushPals runtime overrides
|
|
1789
1805
|
`);
|
|
1790
1806
|
}
|
|
1807
|
+
migrateEmbeddedRuntimeLocalToml(localTomlPath);
|
|
1791
1808
|
}
|
|
1792
1809
|
async function fetchTextFromUrl(url, timeoutMs = 20000) {
|
|
1793
1810
|
const response = await fetchWithTimeout(url, { headers: GITHUB_HEADERS }, timeoutMs);
|
|
@@ -1845,12 +1862,14 @@ async function ensureRuntimeAssets(runtimeRoot, runtimeTag) {
|
|
|
1845
1862
|
writeTextFileIfMissing(join2(runtimeRoot, ".env"), `# Local PushPals runtime environment
|
|
1846
1863
|
`);
|
|
1847
1864
|
const localExamplePath = join2(runtimeRoot, "configs", "local.example.toml");
|
|
1865
|
+
const localTomlPath = join2(runtimeRoot, "configs", "local.toml");
|
|
1848
1866
|
if (existsSync4(localExamplePath)) {
|
|
1849
|
-
writeTextFileIfMissing(
|
|
1867
|
+
writeTextFileIfMissing(localTomlPath, readFileSync4(localExamplePath, "utf8"));
|
|
1850
1868
|
} else {
|
|
1851
|
-
writeTextFileIfMissing(
|
|
1869
|
+
writeTextFileIfMissing(localTomlPath, `# Local PushPals runtime overrides
|
|
1852
1870
|
`);
|
|
1853
1871
|
}
|
|
1872
|
+
migrateEmbeddedRuntimeLocalToml(localTomlPath);
|
|
1854
1873
|
console.log("[pushpals] Embedded runtime assets are ready.");
|
|
1855
1874
|
}
|
|
1856
1875
|
function resolveDeferredRuntimeTagHint(explicitTag) {
|
|
@@ -1895,6 +1914,35 @@ function runtimeBinaryFilename(serviceName, platformKey) {
|
|
|
1895
1914
|
const extension = platformKey.startsWith("windows-") ? ".exe" : "";
|
|
1896
1915
|
return `pushpals-runtime-${serviceToken}-${platformKey}${extension}`;
|
|
1897
1916
|
}
|
|
1917
|
+
function resolveRuntimeBinaryInstallState(runtimeRoot, platformKey) {
|
|
1918
|
+
const binDir = join2(runtimeRoot, "bin", platformKey);
|
|
1919
|
+
const tagMarkerPath = join2(binDir, ".runtime-tag");
|
|
1920
|
+
const installedTag = existsSync4(tagMarkerPath) ? readFileSync4(tagMarkerPath, "utf8").trim() : "";
|
|
1921
|
+
return { binDir, tagMarkerPath, installedTag };
|
|
1922
|
+
}
|
|
1923
|
+
function cleanupLegacyRuntimeBinaryLayouts(runtimeRoot, platformKey, activeBinDir) {
|
|
1924
|
+
const legacyRoot = join2(runtimeRoot, "bin");
|
|
1925
|
+
if (!existsSync4(legacyRoot))
|
|
1926
|
+
return;
|
|
1927
|
+
let entries;
|
|
1928
|
+
try {
|
|
1929
|
+
entries = readdirSync(legacyRoot, { withFileTypes: true });
|
|
1930
|
+
} catch {
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
for (const entry of entries) {
|
|
1934
|
+
if (!entry.isDirectory())
|
|
1935
|
+
continue;
|
|
1936
|
+
const candidateDir = join2(legacyRoot, entry.name);
|
|
1937
|
+
if (candidateDir === activeBinDir)
|
|
1938
|
+
continue;
|
|
1939
|
+
if (!entry.name.endsWith(`-${platformKey}`))
|
|
1940
|
+
continue;
|
|
1941
|
+
try {
|
|
1942
|
+
rmSync(candidateDir, { recursive: true, force: true });
|
|
1943
|
+
} catch {}
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1898
1946
|
function buildEmbeddedRuntimeEnv(baseEnv, opts) {
|
|
1899
1947
|
const env = normalizeChildProcessEnv(baseEnv);
|
|
1900
1948
|
const useRuntimeConfig = opts.useRuntimeConfig !== false;
|
|
@@ -2032,7 +2080,8 @@ async function downloadBinaryAsset(tag, assetName, outPath) {
|
|
|
2032
2080
|
async function ensureRuntimeBinaries(runtimeRoot, runtimeTag) {
|
|
2033
2081
|
const platformKey = resolveRuntimePlatformKey();
|
|
2034
2082
|
console.log(`[pushpals] Preparing embedded runtime binaries for ${runtimeTag} (${platformKey})...`);
|
|
2035
|
-
const
|
|
2083
|
+
const installState = resolveRuntimeBinaryInstallState(runtimeRoot, platformKey);
|
|
2084
|
+
const { binDir, tagMarkerPath, installedTag } = installState;
|
|
2036
2085
|
mkdirSync(binDir, { recursive: true });
|
|
2037
2086
|
const runtimeBinaries = {
|
|
2038
2087
|
server: join2(binDir, runtimeBinaryFilename("server", platformKey)),
|
|
@@ -2048,14 +2097,18 @@ async function ensureRuntimeBinaries(runtimeRoot, runtimeTag) {
|
|
|
2048
2097
|
runtimeBinaries.workerpals,
|
|
2049
2098
|
runtimeBinaries.sourceControlManager
|
|
2050
2099
|
];
|
|
2100
|
+
const shouldRefreshAll = installedTag !== runtimeTag;
|
|
2051
2101
|
let downloadedCount = 0;
|
|
2052
2102
|
for (const binaryPath of requiredAssets) {
|
|
2053
|
-
if (existsSync4(binaryPath))
|
|
2103
|
+
if (!shouldRefreshAll && existsSync4(binaryPath))
|
|
2054
2104
|
continue;
|
|
2055
2105
|
const assetName = binaryPath.split(/[\\/]/).pop() || "";
|
|
2056
2106
|
await downloadBinaryAsset(runtimeTag, assetName, binaryPath);
|
|
2057
2107
|
downloadedCount++;
|
|
2058
2108
|
}
|
|
2109
|
+
writeFileSync(tagMarkerPath, `${runtimeTag}
|
|
2110
|
+
`, "utf8");
|
|
2111
|
+
cleanupLegacyRuntimeBinaryLayouts(runtimeRoot, platformKey, binDir);
|
|
2059
2112
|
if (process.platform !== "win32") {
|
|
2060
2113
|
for (const binaryPath of requiredAssets) {
|
|
2061
2114
|
try {
|
|
@@ -2430,10 +2483,54 @@ async function resolveWorkerpalDockerProbe(cwd, env, platform = process.platform
|
|
|
2430
2483
|
}
|
|
2431
2484
|
var WORKERPAL_SANDBOX_RUNTIME_TAG_LABEL = "pushpals.runtime_tag";
|
|
2432
2485
|
var WORKERPAL_SANDBOX_COMPONENT_LABEL = "pushpals.component=workerpals-sandbox";
|
|
2486
|
+
var WORKERPAL_WARM_COMPONENT_LABEL = "pushpals.component=workerpals-warm";
|
|
2433
2487
|
function resolveConfiguredDockerExecutable(env, platform = process.platform) {
|
|
2434
2488
|
const configured = String(env.PUSHPALS_DOCKER_BIN_ABSOLUTE ?? env.PUSHPALS_DOCKER_BIN ?? (platform === "win32" ? "docker.exe" : "docker")).trim();
|
|
2435
2489
|
return configured || (platform === "win32" ? "docker.exe" : "docker");
|
|
2436
2490
|
}
|
|
2491
|
+
async function cleanupLingeringWorkerpalWarmContainers(opts) {
|
|
2492
|
+
const runCommandWithEnvFn = opts.runCommandWithEnvFn ?? runCommandWithEnv;
|
|
2493
|
+
const dockerExecutable = resolveConfiguredDockerExecutable(opts.env, opts.platform ?? process.platform);
|
|
2494
|
+
const list = await runCommandWithEnvFn([
|
|
2495
|
+
dockerExecutable,
|
|
2496
|
+
"ps",
|
|
2497
|
+
"-aq",
|
|
2498
|
+
"--filter",
|
|
2499
|
+
`label=${WORKERPAL_WARM_COMPONENT_LABEL}`,
|
|
2500
|
+
"--filter",
|
|
2501
|
+
`label=pushpals.repo=${opts.repoRoot}`
|
|
2502
|
+
], opts.repoRoot, opts.env);
|
|
2503
|
+
if (!list.ok) {
|
|
2504
|
+
const detail = list.stderr || list.stdout || `exit ${list.exitCode}`;
|
|
2505
|
+
return {
|
|
2506
|
+
ok: false,
|
|
2507
|
+
detail: `failed to inspect lingering WorkerPal warm containers: ${detail}`,
|
|
2508
|
+
removed: 0
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
const containerIds = list.stdout.split(/\s+/).map((value) => value.trim()).filter(Boolean);
|
|
2512
|
+
if (containerIds.length === 0) {
|
|
2513
|
+
return {
|
|
2514
|
+
ok: true,
|
|
2515
|
+
detail: "no lingering WorkerPal warm containers found",
|
|
2516
|
+
removed: 0
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
const remove = await runCommandWithEnvFn([dockerExecutable, "rm", "-f", ...containerIds], opts.repoRoot, opts.env);
|
|
2520
|
+
if (!remove.ok) {
|
|
2521
|
+
const detail = remove.stderr || remove.stdout || `exit ${remove.exitCode}`;
|
|
2522
|
+
return {
|
|
2523
|
+
ok: false,
|
|
2524
|
+
detail: `failed to remove lingering WorkerPal warm containers: ${detail}`,
|
|
2525
|
+
removed: 0
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
return {
|
|
2529
|
+
ok: true,
|
|
2530
|
+
detail: `removed ${containerIds.length} lingering WorkerPal warm container(s)`,
|
|
2531
|
+
removed: containerIds.length
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2437
2534
|
async function inspectDockerImageRuntimeTag(dockerExecutable, imageName, cwd, env) {
|
|
2438
2535
|
const inspect = await runCommandWithEnv([
|
|
2439
2536
|
dockerExecutable,
|
|
@@ -3919,6 +4016,24 @@ async function main() {
|
|
|
3919
4016
|
let autoStartedServices = [];
|
|
3920
4017
|
let pushpalsLogPath;
|
|
3921
4018
|
let resolvedRuntimeTagForAutoStart = preparedRuntime.runtimeTag || parsed.runtimeTag || "";
|
|
4019
|
+
const cleanupWorkerpalWarmContainersIfNeeded = async (phase) => {
|
|
4020
|
+
if (workerpalDockerPrecheck.status === "failed")
|
|
4021
|
+
return;
|
|
4022
|
+
if (!config.remotebuddy.autoSpawnWorkerpals || !config.remotebuddy.workerpalDocker || !config.remotebuddy.workerpalRequireDocker) {
|
|
4023
|
+
return;
|
|
4024
|
+
}
|
|
4025
|
+
const cleanup = await cleanupLingeringWorkerpalWarmContainers({
|
|
4026
|
+
repoRoot,
|
|
4027
|
+
env: workerpalDockerPrecheck.env
|
|
4028
|
+
});
|
|
4029
|
+
if (!cleanup.ok) {
|
|
4030
|
+
console.warn(`[pushpals] WorkerPal warm-container cleanup warning (${phase}): ${cleanup.detail}`);
|
|
4031
|
+
return;
|
|
4032
|
+
}
|
|
4033
|
+
if (cleanup.removed > 0) {
|
|
4034
|
+
console.log(`[pushpals] ${cleanup.detail} (${phase}).`);
|
|
4035
|
+
}
|
|
4036
|
+
};
|
|
3922
4037
|
const stopAutoStartedServices = () => {
|
|
3923
4038
|
if (autoStartedServices.length === 0)
|
|
3924
4039
|
return;
|
|
@@ -3940,6 +4055,7 @@ async function main() {
|
|
|
3940
4055
|
console.warn(`[pushpals] ${shutdown.detail}`);
|
|
3941
4056
|
}
|
|
3942
4057
|
await stopRuntimeServicesGracefully(services);
|
|
4058
|
+
await cleanupWorkerpalWarmContainersIfNeeded("cli shutdown");
|
|
3943
4059
|
};
|
|
3944
4060
|
let serverHealthy = await probeServer(serverUrl);
|
|
3945
4061
|
const serverWasAlreadyHealthy = serverHealthy;
|
|
@@ -3968,6 +4084,7 @@ async function main() {
|
|
|
3968
4084
|
detail: `No connected RemoteBuddy session consumer found for session ${sessionId}`
|
|
3969
4085
|
};
|
|
3970
4086
|
if (!serverHealthy) {
|
|
4087
|
+
await cleanupWorkerpalWarmContainersIfNeeded("startup preflight");
|
|
3971
4088
|
if (!parsed.noAutoStart) {
|
|
3972
4089
|
try {
|
|
3973
4090
|
const startedRuntime = await autoStartRuntimeServices({
|
|
@@ -4262,8 +4379,10 @@ export {
|
|
|
4262
4379
|
extractRemoteBuddySessionConsumerHealth,
|
|
4263
4380
|
extractRemoteBuddyAutonomousEngineState,
|
|
4264
4381
|
ensureWorkerpalDockerImageReady,
|
|
4382
|
+
ensureRuntimeBinaries,
|
|
4265
4383
|
downloadRuntimeAssetsFromSourceTag,
|
|
4266
4384
|
copyTrackedRepoPath,
|
|
4385
|
+
cleanupLingeringWorkerpalWarmContainers,
|
|
4267
4386
|
bundledMonitoringHubNeedsRefresh,
|
|
4268
4387
|
buildWorkerpalSandboxPaths,
|
|
4269
4388
|
buildServiceStopCommand,
|
package/package.json
CHANGED
|
@@ -40,7 +40,7 @@ status_heartbeat_ms = 120000
|
|
|
40
40
|
workerpal_online_ttl_ms = 15000
|
|
41
41
|
wait_for_workerpal_ms = 15000
|
|
42
42
|
auto_spawn_workerpals = true
|
|
43
|
-
max_workerpals =
|
|
43
|
+
max_workerpals = 3
|
|
44
44
|
workerpal_startup_timeout_ms = 10000
|
|
45
45
|
workerpal_docker = true
|
|
46
46
|
workerpal_require_docker = true
|
|
@@ -143,8 +143,8 @@ docker_warm_max_attempts = 3
|
|
|
143
143
|
docker_warm_retry_backoff_ms = 2000
|
|
144
144
|
docker_job_max_attempts = 2
|
|
145
145
|
docker_job_retry_backoff_ms = 3000
|
|
146
|
-
docker_warm_memory_mb =
|
|
147
|
-
docker_warm_cpus =
|
|
146
|
+
docker_warm_memory_mb = 1024
|
|
147
|
+
docker_warm_cpus = 1
|
|
148
148
|
file_modifying_jobs = ["task.execute"]
|
|
149
149
|
output_max_chars = 196608
|
|
150
150
|
output_max_lines = 600
|
|
@@ -23,7 +23,7 @@ codex_timeout_ms = 120000
|
|
|
23
23
|
reasoning_effort = "high"
|
|
24
24
|
|
|
25
25
|
[remotebuddy]
|
|
26
|
-
max_workerpals =
|
|
26
|
+
max_workerpals = 3
|
|
27
27
|
crash_restart_enabled = true
|
|
28
28
|
crash_restart_max_restarts = 3
|
|
29
29
|
crash_restart_backoff_ms = 3000
|
|
@@ -59,8 +59,8 @@ openhands_stuck_guard_explore_limit = 18
|
|
|
59
59
|
openhands_stuck_guard_min_elapsed_ms = 180000
|
|
60
60
|
openhands_stuck_guard_broad_scan_limit = 2
|
|
61
61
|
openhands_stuck_guard_no_progress_max_ms = 300000
|
|
62
|
-
docker_warm_memory_mb =
|
|
63
|
-
docker_warm_cpus =
|
|
62
|
+
docker_warm_memory_mb = 1024
|
|
63
|
+
docker_warm_cpus = 1
|
|
64
64
|
file_modifying_jobs = ["task.execute"]
|
|
65
65
|
output_max_chars = 196608
|
|
66
66
|
output_max_lines = 600
|
|
@@ -40,7 +40,7 @@ status_heartbeat_ms = 120000
|
|
|
40
40
|
workerpal_online_ttl_ms = 15000
|
|
41
41
|
wait_for_workerpal_ms = 15000
|
|
42
42
|
auto_spawn_workerpals = true
|
|
43
|
-
max_workerpals =
|
|
43
|
+
max_workerpals = 3
|
|
44
44
|
workerpal_startup_timeout_ms = 10000
|
|
45
45
|
workerpal_docker = true
|
|
46
46
|
workerpal_require_docker = true
|
|
@@ -143,8 +143,8 @@ docker_warm_max_attempts = 3
|
|
|
143
143
|
docker_warm_retry_backoff_ms = 2000
|
|
144
144
|
docker_job_max_attempts = 2
|
|
145
145
|
docker_job_retry_backoff_ms = 3000
|
|
146
|
-
docker_warm_memory_mb =
|
|
147
|
-
docker_warm_cpus =
|
|
146
|
+
docker_warm_memory_mb = 1024
|
|
147
|
+
docker_warm_cpus = 1
|
|
148
148
|
file_modifying_jobs = ["task.execute"]
|
|
149
149
|
output_max_chars = 196608
|
|
150
150
|
output_max_lines = 600
|
|
@@ -23,7 +23,7 @@ codex_timeout_ms = 120000
|
|
|
23
23
|
reasoning_effort = "high"
|
|
24
24
|
|
|
25
25
|
[remotebuddy]
|
|
26
|
-
max_workerpals =
|
|
26
|
+
max_workerpals = 3
|
|
27
27
|
crash_restart_enabled = true
|
|
28
28
|
crash_restart_max_restarts = 3
|
|
29
29
|
crash_restart_backoff_ms = 3000
|
|
@@ -59,8 +59,8 @@ openhands_stuck_guard_explore_limit = 18
|
|
|
59
59
|
openhands_stuck_guard_min_elapsed_ms = 180000
|
|
60
60
|
openhands_stuck_guard_broad_scan_limit = 2
|
|
61
61
|
openhands_stuck_guard_no_progress_max_ms = 300000
|
|
62
|
-
docker_warm_memory_mb =
|
|
63
|
-
docker_warm_cpus =
|
|
62
|
+
docker_warm_memory_mb = 1024
|
|
63
|
+
docker_warm_cpus = 1
|
|
64
64
|
file_modifying_jobs = ["task.execute"]
|
|
65
65
|
output_max_chars = 196608
|
|
66
66
|
output_max_lines = 600
|
|
@@ -1508,7 +1508,10 @@ export function loadPushPalsConfig(options: LoadOptions = {}): PushPalsConfig {
|
|
|
1508
1508
|
asBoolean(remoteNode.auto_spawn_workerpals, true),
|
|
1509
1509
|
maxWorkerpals: Math.max(
|
|
1510
1510
|
1,
|
|
1511
|
-
asInt(
|
|
1511
|
+
asInt(
|
|
1512
|
+
parseIntEnv("REMOTEBUDDY_MAX_WORKERPALS") ?? remoteNode.max_workerpals,
|
|
1513
|
+
20,
|
|
1514
|
+
),
|
|
1512
1515
|
),
|
|
1513
1516
|
workerpalStartupTimeoutMs: Math.max(
|
|
1514
1517
|
1_000,
|