@pushpalsdev/cli 1.1.38 → 1.1.39

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.
@@ -2169,6 +2169,97 @@ function withWindowsGitSchannelEnv(env, platform = process.platform) {
2169
2169
  return env;
2170
2170
  return appendGitConfigEnv(env, "http.sslBackend", "schannel");
2171
2171
  }
2172
+ var WINDOWS_NODE_EXTRA_CA_CERTS_DISABLE_ENV = "PUSHPALS_DISABLE_WINDOWS_NODE_EXTRA_CA_CERTS";
2173
+ var WINDOWS_NODE_EXTRA_CA_CERTS_BUNDLE_RELATIVE_PATH = ["certs", "windows-root-ca.pem"];
2174
+ function resolveWindowsNodeExtraCaCertsBundlePath(runtimeRoot) {
2175
+ return join2(runtimeRoot, ...WINDOWS_NODE_EXTRA_CA_CERTS_BUNDLE_RELATIVE_PATH);
2176
+ }
2177
+ function hasUsablePemCertificate(pathValue) {
2178
+ try {
2179
+ return /-----BEGIN CERTIFICATE-----/.test(readFileSync4(pathValue, "utf8"));
2180
+ } catch {
2181
+ return false;
2182
+ }
2183
+ }
2184
+ function ensureWindowsNodeExtraCaCertsBundle(outPath, env) {
2185
+ if (hasUsablePemCertificate(outPath))
2186
+ return outPath;
2187
+ const outDir = dirname(outPath);
2188
+ try {
2189
+ mkdirSync(outDir, { recursive: true });
2190
+ } catch {
2191
+ return "";
2192
+ }
2193
+ const script = String.raw`
2194
+ $ErrorActionPreference = "Stop"
2195
+ $outPath = $env:PUSHPALS_WINDOWS_NODE_EXTRA_CA_CERTS_OUT
2196
+ if (-not $outPath) { throw "PUSHPALS_WINDOWS_NODE_EXTRA_CA_CERTS_OUT is required" }
2197
+ $outDir = Split-Path -Parent $outPath
2198
+ if ($outDir) { [System.IO.Directory]::CreateDirectory($outDir) | Out-Null }
2199
+ $stores = @("Cert:\CurrentUser\Root", "Cert:\LocalMachine\Root")
2200
+ $seen = @{}
2201
+ $lines = New-Object System.Collections.Generic.List[string]
2202
+ foreach ($store in $stores) {
2203
+ if (-not (Test-Path $store)) { continue }
2204
+ foreach ($cert in Get-ChildItem $store) {
2205
+ if (-not $cert.RawData) { continue }
2206
+ if ($cert.NotAfter -lt (Get-Date)) { continue }
2207
+ $thumbprint = [string]$cert.Thumbprint
2208
+ if ($seen.ContainsKey($thumbprint)) { continue }
2209
+ $seen[$thumbprint] = $true
2210
+ $lines.Add("-----BEGIN CERTIFICATE-----")
2211
+ $encoded = [Convert]::ToBase64String($cert.RawData, [Base64FormattingOptions]::InsertLineBreaks)
2212
+ foreach ($line in [regex]::Split($encoded, '\r?\n')) {
2213
+ if ($line) { $lines.Add($line) }
2214
+ }
2215
+ $lines.Add("-----END CERTIFICATE-----")
2216
+ }
2217
+ }
2218
+ if ($lines.Count -eq 0) { throw "No Windows root certificates found" }
2219
+ [System.IO.File]::WriteAllLines($outPath, $lines, [System.Text.Encoding]::ASCII)
2220
+ `;
2221
+ const encodedScript = Buffer.from(script, "utf16le").toString("base64");
2222
+ const childEnv = normalizeChildProcessEnv({
2223
+ ...env,
2224
+ PUSHPALS_WINDOWS_NODE_EXTRA_CA_CERTS_OUT: outPath
2225
+ });
2226
+ const result = Bun.spawnSync([
2227
+ "powershell.exe",
2228
+ "-NoProfile",
2229
+ "-NonInteractive",
2230
+ "-ExecutionPolicy",
2231
+ "Bypass",
2232
+ "-EncodedCommand",
2233
+ encodedScript
2234
+ ], {
2235
+ cwd: process.cwd(),
2236
+ env: childEnv,
2237
+ stdout: "pipe",
2238
+ stderr: "pipe"
2239
+ });
2240
+ if (result.exitCode !== 0)
2241
+ return "";
2242
+ return hasUsablePemCertificate(outPath) ? outPath : "";
2243
+ }
2244
+ function withWindowsNodeExtraCaCertsEnv(env, opts) {
2245
+ const platform = opts.platform ?? process.platform;
2246
+ if (platform !== "win32")
2247
+ return env;
2248
+ if (parseBooleanFlag(env[WINDOWS_NODE_EXTRA_CA_CERTS_DISABLE_ENV]) === true)
2249
+ return env;
2250
+ if (typeof env.NODE_EXTRA_CA_CERTS === "string" && env.NODE_EXTRA_CA_CERTS.trim())
2251
+ return env;
2252
+ const runtimeRoot = String(opts.runtimeRoot ?? "").trim();
2253
+ if (!runtimeRoot || !existsSync5(runtimeRoot))
2254
+ return env;
2255
+ const bundlePath = ensureWindowsNodeExtraCaCertsBundle(resolveWindowsNodeExtraCaCertsBundlePath(runtimeRoot), env);
2256
+ if (!bundlePath)
2257
+ return env;
2258
+ return {
2259
+ ...env,
2260
+ NODE_EXTRA_CA_CERTS: bundlePath
2261
+ };
2262
+ }
2172
2263
  async function runGitWithEnv(args, cwd, env, timeoutMs) {
2173
2264
  return await runCommandWithEnv(["git", ...args], cwd, withWindowsGitSchannelEnv(env), timeoutMs);
2174
2265
  }
@@ -2844,7 +2935,11 @@ function buildEmbeddedRuntimeEnv(baseEnv, opts) {
2844
2935
  ...typeof env.PUSHPALS_DOCKER_BIN === "string" && env.PUSHPALS_DOCKER_BIN.trim() ? { PUSHPALS_DOCKER_BIN: env.PUSHPALS_DOCKER_BIN.trim() } : {},
2845
2936
  ...typeof env.PUSHPALS_DOCKER_BIN_ABSOLUTE === "string" && env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() ? { PUSHPALS_DOCKER_BIN_ABSOLUTE: env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() } : {}
2846
2937
  };
2847
- return withWindowsGitSchannelEnv(runtimeEnv, platform);
2938
+ const runtimeEnvWithWindowsCa = withWindowsNodeExtraCaCertsEnv(runtimeEnv, {
2939
+ platform,
2940
+ runtimeRoot: opts.runtimeRoot
2941
+ });
2942
+ return withWindowsGitSchannelEnv(runtimeEnvWithWindowsCa, platform);
2848
2943
  }
2849
2944
  function parseBooleanFlag(raw) {
2850
2945
  const normalized = String(raw ?? "").trim().toLowerCase();
@@ -6034,10 +6129,13 @@ ${line}
6034
6129
  console.log("[pushpals] Runtime-only mode is active. Send `exit` on stdin or terminate the process to stop.");
6035
6130
  await new Promise((resolveStop) => {
6036
6131
  let resolved = false;
6132
+ let exitRequestedFromInput = false;
6133
+ const keepAlive = setInterval(() => {}, 60000);
6037
6134
  const finish = () => {
6038
6135
  if (resolved)
6039
6136
  return;
6040
6137
  resolved = true;
6138
+ clearInterval(keepAlive);
6041
6139
  resolveStop();
6042
6140
  };
6043
6141
  process.once("SIGINT", finish);
@@ -6050,13 +6148,17 @@ ${line}
6050
6148
  runtimeOnlyInput.on("line", (line) => {
6051
6149
  if (!isCliExitCommand(line))
6052
6150
  return;
6151
+ exitRequestedFromInput = true;
6053
6152
  requestStop();
6054
6153
  runtimeOnlyInput.close();
6055
6154
  finish();
6056
6155
  });
6057
6156
  runtimeOnlyInput.on("close", () => {
6058
- requestStop();
6059
- finish();
6157
+ if (exitRequestedFromInput || resolved) {
6158
+ finish();
6159
+ return;
6160
+ }
6161
+ console.log("[pushpals] Runtime-only stdin closed; continuing until terminated.");
6060
6162
  });
6061
6163
  });
6062
6164
  await requestStop();
@@ -6138,6 +6240,7 @@ if (import.meta.main) {
6138
6240
  });
6139
6241
  }
6140
6242
  export {
6243
+ withWindowsNodeExtraCaCertsEnv,
6141
6244
  waitForWorkerpalCapacity,
6142
6245
  waitForRemoteBuddySessionConsumer,
6143
6246
  startEmbeddedMonitoringHub,
@@ -6154,6 +6257,7 @@ export {
6154
6257
  resolveWorkerExecutionReadiness,
6155
6258
  resolveWindowsWhereExecutableCandidatesForEnv,
6156
6259
  resolveWindowsShellExecutableCandidatesForEnv,
6260
+ resolveWindowsNodeExtraCaCertsBundlePath,
6157
6261
  resolveWindowsFreshRuntimeWorkerpalPrewarmDelayMs,
6158
6262
  resolveRuntimeGitExecutableCandidates,
6159
6263
  resolveRuntimeDockerExecutableCandidates,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushpalsdev/cli",
3
- "version": "1.1.38",
3
+ "version": "1.1.39",
4
4
  "description": "PushPals terminal CLI for LocalBuddy -> RemoteBuddy orchestration",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -127,6 +127,7 @@ _SMALL_TASK_ROLLOUT_WATCHDOG_S = 240
127
127
  _NARROW_TEST_TASK_ROLLOUT_WATCHDOG_S = 150
128
128
  _WEB_REVIEW_ROLLOUT_WATCHDOG_S = 180
129
129
  _BACKGROUND_ROLLOUT_WATCHDOG_S = 90
130
+ _MIN_AUTO_WATCHDOG_TIMEOUT_S = 180
130
131
  _MIN_CODEX_RECOVERY_ATTEMPT_S = 120
131
132
  _NO_PUBLISHABLE_FAILURE_COOLDOWN_MS = 10 * 60 * 1000
132
133
  _CODEX_STARTUP_ONLY_EVENT_TYPES = {"thread.started", "turn.started"}
@@ -664,6 +665,9 @@ def _looks_like_narrow_test_task_prompt(prompt: str) -> bool:
664
665
  "ranking contract",
665
666
  "regression coverage",
666
667
  "focused coverage",
668
+ "focused test",
669
+ "focused tests",
670
+ "focused testing",
667
671
  "focused regression",
668
672
  "test-only",
669
673
  "test only",
@@ -742,7 +746,7 @@ def _resolve_no_edit_watchdog_seconds(
742
746
  else:
743
747
  return max(1, min(parsed, max(1, communicate_timeout_s - 1)))
744
748
 
745
- if communicate_timeout_s < 600:
749
+ if communicate_timeout_s < _MIN_AUTO_WATCHDOG_TIMEOUT_S:
746
750
  return None
747
751
 
748
752
  prompt_text = str(prompt or "").lower()
@@ -887,7 +891,7 @@ def _resolve_rollout_watchdog_seconds(
887
891
  communicate_timeout_s: Optional[int],
888
892
  no_edit_watchdog_s: Optional[int],
889
893
  ) -> Optional[int]:
890
- if not communicate_timeout_s or communicate_timeout_s < 600:
894
+ if not communicate_timeout_s or communicate_timeout_s < _MIN_AUTO_WATCHDOG_TIMEOUT_S:
891
895
  return None
892
896
 
893
897
  raw = os.environ.get("WORKERPALS_OPENAI_CODEX_ROLLOUT_WATCHDOG_S", "").strip()
@@ -2156,6 +2156,24 @@ class OpenAICodexRuntimeConfigTests(unittest.TestCase):
2156
2156
 
2157
2157
  self.assertEqual(watchdog_s, 180)
2158
2158
 
2159
+ def test_review_fix_child_budget_below_ten_minutes_still_uses_watchdogs(self) -> None:
2160
+ prompt = (
2161
+ "Rejected PR revision brief: Previous ReviewAgent score: 8.0 / 10. "
2162
+ "Add focused tests for createCleanupHarness.runTask covering successful execution, "
2163
+ "execute failure, cleanup failure, invalid task input, and cleanup execution after "
2164
+ "successful task completion."
2165
+ )
2166
+ env = {
2167
+ "WORKERPALS_OPENAI_CODEX_NO_EDIT_WATCHDOG_S": "",
2168
+ "WORKERPALS_OPENAI_CODEX_ROLLOUT_WATCHDOG_S": "",
2169
+ }
2170
+ with mock.patch.dict(os.environ, env, clear=False):
2171
+ no_edit_s = _resolve_no_edit_watchdog_seconds(prompt, 570)
2172
+ rollout_s = _resolve_rollout_watchdog_seconds(prompt, 570, no_edit_s)
2173
+
2174
+ self.assertEqual(no_edit_s, 180)
2175
+ self.assertEqual(rollout_s, 120)
2176
+
2159
2177
  def test_no_edit_recovery_guidance_warns_against_artifact_only_progress(self) -> None:
2160
2178
  guidance = _build_no_edit_recovery_guidance(
2161
2179
  "item.completed | still inspecting",