@pushpalsdev/cli 1.0.24 → 1.0.25

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.
@@ -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),
@@ -2430,10 +2430,54 @@ async function resolveWorkerpalDockerProbe(cwd, env, platform = process.platform
2430
2430
  }
2431
2431
  var WORKERPAL_SANDBOX_RUNTIME_TAG_LABEL = "pushpals.runtime_tag";
2432
2432
  var WORKERPAL_SANDBOX_COMPONENT_LABEL = "pushpals.component=workerpals-sandbox";
2433
+ var WORKERPAL_WARM_COMPONENT_LABEL = "pushpals.component=workerpals-warm";
2433
2434
  function resolveConfiguredDockerExecutable(env, platform = process.platform) {
2434
2435
  const configured = String(env.PUSHPALS_DOCKER_BIN_ABSOLUTE ?? env.PUSHPALS_DOCKER_BIN ?? (platform === "win32" ? "docker.exe" : "docker")).trim();
2435
2436
  return configured || (platform === "win32" ? "docker.exe" : "docker");
2436
2437
  }
2438
+ async function cleanupLingeringWorkerpalWarmContainers(opts) {
2439
+ const runCommandWithEnvFn = opts.runCommandWithEnvFn ?? runCommandWithEnv;
2440
+ const dockerExecutable = resolveConfiguredDockerExecutable(opts.env, opts.platform ?? process.platform);
2441
+ const list = await runCommandWithEnvFn([
2442
+ dockerExecutable,
2443
+ "ps",
2444
+ "-aq",
2445
+ "--filter",
2446
+ `label=${WORKERPAL_WARM_COMPONENT_LABEL}`,
2447
+ "--filter",
2448
+ `label=pushpals.repo=${opts.repoRoot}`
2449
+ ], opts.repoRoot, opts.env);
2450
+ if (!list.ok) {
2451
+ const detail = list.stderr || list.stdout || `exit ${list.exitCode}`;
2452
+ return {
2453
+ ok: false,
2454
+ detail: `failed to inspect lingering WorkerPal warm containers: ${detail}`,
2455
+ removed: 0
2456
+ };
2457
+ }
2458
+ const containerIds = list.stdout.split(/\s+/).map((value) => value.trim()).filter(Boolean);
2459
+ if (containerIds.length === 0) {
2460
+ return {
2461
+ ok: true,
2462
+ detail: "no lingering WorkerPal warm containers found",
2463
+ removed: 0
2464
+ };
2465
+ }
2466
+ const remove = await runCommandWithEnvFn([dockerExecutable, "rm", "-f", ...containerIds], opts.repoRoot, opts.env);
2467
+ if (!remove.ok) {
2468
+ const detail = remove.stderr || remove.stdout || `exit ${remove.exitCode}`;
2469
+ return {
2470
+ ok: false,
2471
+ detail: `failed to remove lingering WorkerPal warm containers: ${detail}`,
2472
+ removed: 0
2473
+ };
2474
+ }
2475
+ return {
2476
+ ok: true,
2477
+ detail: `removed ${containerIds.length} lingering WorkerPal warm container(s)`,
2478
+ removed: containerIds.length
2479
+ };
2480
+ }
2437
2481
  async function inspectDockerImageRuntimeTag(dockerExecutable, imageName, cwd, env) {
2438
2482
  const inspect = await runCommandWithEnv([
2439
2483
  dockerExecutable,
@@ -3919,6 +3963,24 @@ async function main() {
3919
3963
  let autoStartedServices = [];
3920
3964
  let pushpalsLogPath;
3921
3965
  let resolvedRuntimeTagForAutoStart = preparedRuntime.runtimeTag || parsed.runtimeTag || "";
3966
+ const cleanupWorkerpalWarmContainersIfNeeded = async (phase) => {
3967
+ if (workerpalDockerPrecheck.status === "failed")
3968
+ return;
3969
+ if (!config.remotebuddy.autoSpawnWorkerpals || !config.remotebuddy.workerpalDocker || !config.remotebuddy.workerpalRequireDocker) {
3970
+ return;
3971
+ }
3972
+ const cleanup = await cleanupLingeringWorkerpalWarmContainers({
3973
+ repoRoot,
3974
+ env: workerpalDockerPrecheck.env
3975
+ });
3976
+ if (!cleanup.ok) {
3977
+ console.warn(`[pushpals] WorkerPal warm-container cleanup warning (${phase}): ${cleanup.detail}`);
3978
+ return;
3979
+ }
3980
+ if (cleanup.removed > 0) {
3981
+ console.log(`[pushpals] ${cleanup.detail} (${phase}).`);
3982
+ }
3983
+ };
3922
3984
  const stopAutoStartedServices = () => {
3923
3985
  if (autoStartedServices.length === 0)
3924
3986
  return;
@@ -3940,6 +4002,7 @@ async function main() {
3940
4002
  console.warn(`[pushpals] ${shutdown.detail}`);
3941
4003
  }
3942
4004
  await stopRuntimeServicesGracefully(services);
4005
+ await cleanupWorkerpalWarmContainersIfNeeded("cli shutdown");
3943
4006
  };
3944
4007
  let serverHealthy = await probeServer(serverUrl);
3945
4008
  const serverWasAlreadyHealthy = serverHealthy;
@@ -3968,6 +4031,7 @@ async function main() {
3968
4031
  detail: `No connected RemoteBuddy session consumer found for session ${sessionId}`
3969
4032
  };
3970
4033
  if (!serverHealthy) {
4034
+ await cleanupWorkerpalWarmContainersIfNeeded("startup preflight");
3971
4035
  if (!parsed.noAutoStart) {
3972
4036
  try {
3973
4037
  const startedRuntime = await autoStartRuntimeServices({
@@ -4264,6 +4328,7 @@ export {
4264
4328
  ensureWorkerpalDockerImageReady,
4265
4329
  downloadRuntimeAssetsFromSourceTag,
4266
4330
  copyTrackedRepoPath,
4331
+ cleanupLingeringWorkerpalWarmContainers,
4267
4332
  bundledMonitoringHubNeedsRefresh,
4268
4333
  buildWorkerpalSandboxPaths,
4269
4334
  buildServiceStopCommand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushpalsdev/cli",
3
- "version": "1.0.24",
3
+ "version": "1.0.25",
4
4
  "description": "PushPals terminal CLI for LocalBuddy -> RemoteBuddy orchestration",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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 = 10
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 = 2048
147
- docker_warm_cpus = 2
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 = 10
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 = 2048
63
- docker_warm_cpus = 2
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 = 10
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 = 2048
147
- docker_warm_cpus = 2
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 = 10
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 = 2048
63
- docker_warm_cpus = 2
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(remoteNode.max_workerpals, 20),
1511
+ asInt(
1512
+ parseIntEnv("REMOTEBUDDY_MAX_WORKERPALS") ?? remoteNode.max_workerpals,
1513
+ 20,
1514
+ ),
1512
1515
  ),
1513
1516
  workerpalStartupTimeoutMs: Math.max(
1514
1517
  1_000,