@pushpalsdev/cli 1.1.21 → 1.1.23

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.
@@ -18,7 +18,7 @@ import { homedir } from "os";
18
18
  import { isAbsolute, relative, resolve } from "path";
19
19
  import { loadPushPalsConfig } from "shared";
20
20
  import { resolveExecutor, type WorkerpalsRuntimeConfig } from "./common/executor_backend.js";
21
- import type { ExecutorBackend } from "./common/types.js";
21
+ import type { ExecutorBackend, JobDiagnostics } from "./common/types.js";
22
22
  import { computeTimeoutWarningWindow, DEFAULT_DOCKER_TIMEOUT_MS } from "./timeout_policy.js";
23
23
  import {
24
24
  BACKEND_DOCKER_PASSTHROUGH_ENV,
@@ -237,6 +237,7 @@ export interface DockerJobResult {
237
237
  branch: string;
238
238
  sha: string;
239
239
  };
240
+ diagnostics?: JobDiagnostics;
240
241
  }
241
242
 
242
243
  export interface Job {
@@ -247,6 +248,37 @@ export interface Job {
247
248
  sessionId: string;
248
249
  }
249
250
 
251
+ function compactDockerDiagnosticText(value: unknown, maxChars = 1000): string | null {
252
+ const text = String(value ?? "").replace(/\s+$/g, "").trim();
253
+ if (!text) return null;
254
+ return text.length <= maxChars ? text : text.slice(0, maxChars);
255
+ }
256
+
257
+ function dockerFallbackDiagnostics(
258
+ summary: string,
259
+ context: { timedOutByDocker: boolean; elapsedMs: number; timeoutMs: number },
260
+ exitCode: number,
261
+ failureClass: string,
262
+ metadata: Record<string, unknown> = {},
263
+ ): JobDiagnostics {
264
+ return {
265
+ terminal: {
266
+ failureClass,
267
+ terminalStage: "docker",
268
+ summary: compactDockerDiagnosticText(summary),
269
+ watchdogFired: context.timedOutByDocker,
270
+ timeoutMs: context.timeoutMs,
271
+ metadata: {
272
+ structuredResult: false,
273
+ elapsedMs: context.elapsedMs,
274
+ exitCode,
275
+ timedOutByDocker: context.timedOutByDocker,
276
+ ...metadata,
277
+ },
278
+ },
279
+ };
280
+ }
281
+
250
282
  function readPositiveNumber(value: unknown): number | null {
251
283
  const parsed =
252
284
  typeof value === "number"
@@ -264,6 +296,10 @@ function maybeRecord(value: unknown): Record<string, unknown> | null {
264
296
  : null;
265
297
  }
266
298
 
299
+ function isReadableByteStream(value: unknown): value is ReadableStream<Uint8Array> {
300
+ return value instanceof ReadableStream;
301
+ }
302
+
267
303
  function collectValidationCommandHints(params: Record<string, unknown>): string[] {
268
304
  const planning = maybeRecord(params.planning);
269
305
  const values: unknown[] = [
@@ -1280,10 +1316,15 @@ export class DockerExecutor {
1280
1316
  const stderrLines: string[] = [];
1281
1317
 
1282
1318
  try {
1319
+ const stdout = proc.stdout;
1320
+ const stderr = proc.stderr;
1321
+ if (!isReadableByteStream(stdout) || !isReadableByteStream(stderr)) {
1322
+ throw new Error("docker exec stdout/stderr pipes were not available");
1323
+ }
1283
1324
  await Promise.all([
1284
1325
  this.writeJobSpecToStdin(proc, base64Spec),
1285
- this.readStream(proc.stdout, "stdout", onLog, stdoutLines),
1286
- this.readStream(proc.stderr, "stderr", onLog, stderrLines),
1326
+ this.readStream(stdout, "stdout", onLog, stdoutLines),
1327
+ this.readStream(stderr, "stderr", onLog, stderrLines),
1287
1328
  ]);
1288
1329
  } catch (err) {
1289
1330
  try {
@@ -1343,7 +1384,7 @@ export class DockerExecutor {
1343
1384
  throw new Error("docker exec stdin pipe was not available");
1344
1385
  }
1345
1386
  const bytes = new TextEncoder().encode(base64Spec);
1346
- if ("getWriter" in stdin && typeof stdin.getWriter === "function") {
1387
+ if (stdin instanceof WritableStream) {
1347
1388
  const writer = stdin.getWriter();
1348
1389
  try {
1349
1390
  await writer.write(bytes);
@@ -1359,12 +1400,17 @@ export class DockerExecutor {
1359
1400
  return;
1360
1401
  }
1361
1402
 
1362
- if (typeof stdin.write === "function" && typeof stdin.end === "function") {
1363
- await stdin.write(bytes);
1364
- if (typeof stdin.flush === "function") {
1365
- await stdin.flush();
1403
+ const nodeStdin = stdin as {
1404
+ write?: (chunk: Uint8Array | string) => unknown;
1405
+ end?: () => unknown;
1406
+ flush?: () => unknown;
1407
+ };
1408
+ if (typeof nodeStdin.write === "function" && typeof nodeStdin.end === "function") {
1409
+ await nodeStdin.write(bytes);
1410
+ if (typeof nodeStdin.flush === "function") {
1411
+ await nodeStdin.flush();
1366
1412
  }
1367
- await stdin.end();
1413
+ await nodeStdin.end();
1368
1414
  return;
1369
1415
  }
1370
1416
 
@@ -1648,44 +1694,57 @@ export class DockerExecutor {
1648
1694
  `Malformed ___RESULT___ payload: ${sentinelParseError || "unknown parse error"}`,
1649
1695
  ];
1650
1696
  if (stderr) details.push(stderr);
1697
+ const summary = `Worker returned malformed structured result after ${context.elapsedMs}ms`;
1651
1698
  return {
1652
1699
  ok: false,
1653
- summary: `Worker returned malformed structured result after ${context.elapsedMs}ms`,
1700
+ summary,
1654
1701
  stdout,
1655
1702
  stderr: details.join("\n"),
1656
1703
  exitCode,
1704
+ diagnostics: dockerFallbackDiagnostics(summary, context, exitCode, "malformed_structured_result", {
1705
+ sentinelParseError,
1706
+ }),
1657
1707
  };
1658
1708
  }
1659
1709
 
1660
1710
  // No sentinel found, return generic result.
1661
1711
  if (context.timedOutByDocker) {
1712
+ const summary = `Job timed out in Docker executor after ${context.elapsedMs}ms (limit ${context.timeoutMs}ms; terminated before structured result).`;
1662
1713
  return {
1663
1714
  ok: false,
1664
- summary: `Job timed out in Docker executor after ${context.elapsedMs}ms (limit ${context.timeoutMs}ms; terminated before structured result).`,
1715
+ summary,
1665
1716
  stdout,
1666
1717
  stderr,
1667
1718
  exitCode,
1719
+ diagnostics: dockerFallbackDiagnostics(summary, context, exitCode, "timeout"),
1668
1720
  };
1669
1721
  }
1670
1722
  if (exitCode === 143 || exitCode === 137) {
1723
+ const summary = `Job process was terminated (exit ${exitCode}) after ${context.elapsedMs}ms before structured result was produced.`;
1671
1724
  return {
1672
1725
  ok: false,
1673
- summary: `Job process was terminated (exit ${exitCode}) after ${context.elapsedMs}ms before structured result was produced.`,
1726
+ summary,
1674
1727
  stdout,
1675
1728
  stderr,
1676
1729
  exitCode,
1730
+ diagnostics: dockerFallbackDiagnostics(summary, context, exitCode, "terminated"),
1677
1731
  };
1678
1732
  }
1679
1733
 
1734
+ const summary =
1735
+ exitCode === 0
1736
+ ? `Job completed in ${context.elapsedMs}ms`
1737
+ : `Job failed (exit ${exitCode}, elapsed ${context.elapsedMs}ms)`;
1680
1738
  return {
1681
1739
  ok: exitCode === 0,
1682
- summary:
1683
- exitCode === 0
1684
- ? `Job completed in ${context.elapsedMs}ms`
1685
- : `Job failed (exit ${exitCode}, elapsed ${context.elapsedMs}ms)`,
1740
+ summary,
1686
1741
  stdout,
1687
1742
  stderr,
1688
1743
  exitCode,
1744
+ diagnostics:
1745
+ exitCode === 0
1746
+ ? undefined
1747
+ : dockerFallbackDiagnostics(summary, context, exitCode, "no_structured_result"),
1689
1748
  };
1690
1749
  }
1691
1750