@pushpalsdev/cli 1.1.31 → 1.1.34

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushpalsdev/cli",
3
- "version": "1.1.31",
3
+ "version": "1.1.34",
4
4
  "description": "PushPals terminal CLI for LocalBuddy -> RemoteBuddy orchestration",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -350,6 +350,28 @@ class OpenAICodexRuntimeConfigTests(unittest.TestCase):
350
350
  self.assertIn("Home shell startup is assertable", guidance)
351
351
  self.assertIn("bun run web:e2e", guidance)
352
352
 
353
+ def test_parse_payload_accepts_file_backed_payload_transport(self) -> None:
354
+ with tempfile.TemporaryDirectory(prefix="pushpals-payload-file-") as temp_dir:
355
+ repo = Path(temp_dir) / "repo"
356
+ repo.mkdir(parents=True, exist_ok=True)
357
+ payload = {
358
+ "kind": "task.execute",
359
+ "repo": str(repo),
360
+ "params": {"instruction": "Make one small publishable change"},
361
+ }
362
+ encoded = base64.b64encode(json.dumps(payload).encode("utf-8")).decode("ascii")
363
+ payload_file = Path(temp_dir) / "payload.b64"
364
+ payload_file.write_text(encoded, encoding="utf-8")
365
+
366
+ task = parse_task_execute_payload(
367
+ ["executor", "--payload-file", str(payload_file)],
368
+ logger=Logger("[test]"),
369
+ )
370
+
371
+ self.assertEqual(task.kind, "task.execute")
372
+ self.assertEqual(task.repo, str(repo.resolve()))
373
+ self.assertEqual(task.instruction, "Make one small publishable change")
374
+
353
375
  def test_parse_payload_prefers_helper_tests_for_visual_derivation_tasks(self) -> None:
354
376
  with tempfile.TemporaryDirectory(prefix="pushpals-visual-guidance-") as temp_dir:
355
377
  repo = Path(temp_dir) / "repo"
@@ -17,6 +17,10 @@ import {
17
17
  filterResultLines,
18
18
  } from "../common/execution_utils.js";
19
19
  import { buildWorkerSandboxWritableEnv } from "../common/sandbox_env.js";
20
+ import {
21
+ createPythonPayloadTransport,
22
+ type PythonPayloadTransport,
23
+ } from "../common/python_payload_transport.js";
20
24
  import { computeTimeoutWarningWindow } from "../timeout_policy.js";
21
25
 
22
26
  // ---- Script path (resolved relative to this file) ----------------------------
@@ -274,6 +278,7 @@ export async function executeWithOpenHands(
274
278
  let timeoutTimer: ReturnType<typeof setTimeout> | null = null;
275
279
  let stuckNudgeStartTimer: ReturnType<typeof setTimeout> | null = null;
276
280
  let stuckNudgeTimer: ReturnType<typeof setInterval> | null = null;
281
+ let payloadTransport: PythonPayloadTransport | null = null;
277
282
  const outputPolicy = {
278
283
  maxOutputChars: runtimeConfig.workerpals.outputMaxChars,
279
284
  maxOutputLines: runtimeConfig.workerpals.outputMaxLines,
@@ -282,7 +287,8 @@ export async function executeWithOpenHands(
282
287
  };
283
288
 
284
289
  try {
285
- const proc = Bun.spawn([pythonBin, scriptPath, payload], {
290
+ payloadTransport = createPythonPayloadTransport(payload);
291
+ const proc = Bun.spawn([pythonBin, scriptPath, ...payloadTransport.args], {
286
292
  cwd: repo,
287
293
  stdout: "pipe",
288
294
  stderr: "pipe",
@@ -623,5 +629,6 @@ export async function executeWithOpenHands(
623
629
  if (stuckNudgeTimer) {
624
630
  clearInterval(stuckNudgeTimer);
625
631
  }
632
+ payloadTransport?.cleanup();
626
633
  }
627
634
  }
@@ -163,6 +163,20 @@ def decode_payload(raw: str) -> Dict[str, Any]:
163
163
  return payload
164
164
 
165
165
 
166
+ def read_encoded_payload_arg(argv: List[str]) -> str:
167
+ if len(argv) < 2:
168
+ raise ValueError("missing base64 job payload")
169
+ mode = argv[1]
170
+ if mode == "--payload-file":
171
+ if len(argv) < 3 or not str(argv[2] or "").strip():
172
+ raise ValueError("missing payload file path")
173
+ path = Path(str(argv[2])).expanduser()
174
+ return path.read_text(encoding="utf-8").strip()
175
+ if mode == "--payload-stdin":
176
+ return sys.stdin.read().strip()
177
+ return mode
178
+
179
+
166
180
  def resolve_repo_within_assigned_root(repo: str) -> Tuple[Optional[str], Optional[str]]:
167
181
  raw_repo = str(repo or "").strip()
168
182
  if not raw_repo:
@@ -968,11 +982,8 @@ def parse_task_execute_payload(
968
982
  don't need to handle them.
969
983
  """
970
984
  log = logger or Logger("[Executor]")
971
- if len(argv) < 2:
972
- raise SystemExit(fail("Missing base64 job payload", exit_code=2))
973
-
974
985
  try:
975
- payload = decode_payload(argv[1])
986
+ payload = decode_payload(read_encoded_payload_arg(argv))
976
987
  except Exception as exc:
977
988
  raise SystemExit(fail(f"Failed to decode job payload: {exc}", exit_code=2))
978
989
 
@@ -19,6 +19,10 @@ import {
19
19
  streamLines,
20
20
  } from "./execution_utils.js";
21
21
  import { buildWorkerSandboxWritableEnv } from "./sandbox_env.js";
22
+ import {
23
+ createPythonPayloadTransport,
24
+ type PythonPayloadTransport,
25
+ } from "./python_payload_transport.js";
22
26
 
23
27
  interface GenericPythonExecutorConfig {
24
28
  backendName: string;
@@ -357,7 +361,6 @@ export function createGenericPythonExecutor(
357
361
  }),
358
362
  "utf-8",
359
363
  ).toString("base64");
360
- const args = [pythonBin, scriptPath, payloadBase64];
361
364
  const childTimeoutMs = resolveGenericPythonExecutorChildTimeoutMs({
362
365
  backendName,
363
366
  hostTimeoutMs: timeoutMs,
@@ -379,12 +382,15 @@ export function createGenericPythonExecutor(
379
382
  )}ms`
380
383
  : "";
381
384
 
382
- onLog?.(
383
- "stdout",
384
- `[${backendLabel}Executor] Spawning ${backendName} executor (timeout=${timeoutMs}ms; ${timeoutDetail}${childTimeoutDetail})`,
385
- );
386
-
385
+ let payloadTransport: PythonPayloadTransport | null = null;
387
386
  try {
387
+ payloadTransport = createPythonPayloadTransport(payloadBase64);
388
+ const args = [pythonBin, scriptPath, ...payloadTransport.args];
389
+ onLog?.(
390
+ "stdout",
391
+ `[${backendLabel}Executor] Spawning ${backendName} executor (timeout=${timeoutMs}ms; ${timeoutDetail}${childTimeoutDetail})`,
392
+ );
393
+
388
394
  const outputPolicy = {
389
395
  maxOutputChars: runtimeConfig.workerpals.outputMaxChars,
390
396
  maxOutputLines: runtimeConfig.workerpals.outputMaxLines,
@@ -539,6 +545,8 @@ export function createGenericPythonExecutor(
539
545
  "",
540
546
  ),
541
547
  };
548
+ } finally {
549
+ payloadTransport?.cleanup();
542
550
  }
543
551
  };
544
552
  }
@@ -0,0 +1,26 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from "fs";
2
+ import { tmpdir } from "os";
3
+ import { join } from "path";
4
+
5
+ export interface PythonPayloadTransport {
6
+ args: string[];
7
+ filePath: string;
8
+ cleanup: () => void;
9
+ }
10
+
11
+ export function createPythonPayloadTransport(payloadBase64: string): PythonPayloadTransport {
12
+ const dir = mkdtempSync(join(tmpdir(), "pushpals-python-payload-"));
13
+ const filePath = join(dir, "payload.b64");
14
+ writeFileSync(filePath, payloadBase64, { encoding: "utf8", mode: 0o600 });
15
+
16
+ let cleaned = false;
17
+ return {
18
+ args: ["--payload-file", filePath],
19
+ filePath,
20
+ cleanup: () => {
21
+ if (cleaned) return;
22
+ cleaned = true;
23
+ rmSync(dir, { recursive: true, force: true });
24
+ },
25
+ };
26
+ }
@@ -9,6 +9,7 @@
9
9
  "cli": "bun run scripts/pushpals-cli.ts",
10
10
  "cli:integration": "bun run scripts/cli-integration.ts",
11
11
  "cli:bundle": "bun run --cwd packages/cli build",
12
+ "cli:verify-package-payload": "bun run scripts/verify-cli-package-payload.ts",
12
13
  "cli:monitor:export": "bun run scripts/sync-cli-monitor-ui.ts",
13
14
  "replay:worker-job": "bun run scripts/replay-worker-job.ts",
14
15
  "protocol:build": "bun --cwd packages/protocol build",