@aicopycoders/exodus 2026.8.1100 → 2026.8.1200

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.
@@ -4,7 +4,7 @@ import nodePath from "node:path";
4
4
  import { apiGet, apiGetText, apiPost, apiPostDashboard, getDashboardUrl, } from "../lib/client.js";
5
5
  import { formatApiError } from "../lib/format.js";
6
6
  import { pollUntilDone } from "../lib/poll.js";
7
- import { normalizeRunStatus, runStatusLabel, storedWorkflowStatusForms, TERMINAL_RUN_STATUSES, } from "../lib/runStatus.js";
7
+ import { normalizeRunStatus, runStatusLabel, storedWorkflowStatusForms, workflowRunPresentation, RUN_STATUS_LABELS, TERMINAL_RUN_STATUSES, } from "../lib/runStatus.js";
8
8
  import { workflowToYaml, parseWorkflowText } from "../lib/workflowText.js";
9
9
  import { missingRouteLine } from "../lib/route-support.js";
10
10
  import { getChannel } from "../lib/channel.js";
@@ -429,6 +429,17 @@ function gateParkedNodeId(status, pauseReason, pausedNodeId) {
429
429
  }
430
430
  return undefined;
431
431
  }
432
+ function runVerdict(run) {
433
+ const presented = workflowRunPresentation(run.status, run.pauseReason);
434
+ if ((presented.status === "succeeded" || presented.status === "succeeded-with-warnings") &&
435
+ (run.counts?.failed ?? 0) > 0 &&
436
+ run.deliveries !== undefined &&
437
+ run.deliveries.length > 0 &&
438
+ !run.deliveries.some((d) => d.status === "delivered")) {
439
+ return `${RUN_STATUS_LABELS.failed} — nothing delivered`;
440
+ }
441
+ return presented.detail ? `${presented.label} — ${presented.detail}` : presented.label;
442
+ }
432
443
  const RETIRED_GATE_VERB_POINTER = "The Gate node retired in 2.0 — runs now pause at a Checkpoint box on the canvas. " +
433
444
  "Use: exodus workflow checkpoint <runId> [approve|edit|retry|cancel]";
434
445
  export function formatPauseNotice(pauseReason, runId, dashboardUrl) {
@@ -948,7 +959,7 @@ export function formatWorkflowList(workflows) {
948
959
  export function formatRecentRuns(runs) {
949
960
  if (runs.length === 0)
950
961
  return "No workflow runs found for the active brand.";
951
- return table(["workflow", "status", "created", "id"], runs.map((r) => [r.workflowName, runStatusLabel(r.status), dateOnly(r.createdAt), r._id]));
962
+ return table(["workflow", "status", "created", "id"], runs.map((r) => [r.workflowName, runVerdict(r), dateOnly(r.createdAt), r._id]));
952
963
  }
953
964
  export function formatWorkflowVersions(versions) {
954
965
  if (versions.length === 0) {
@@ -1026,7 +1037,7 @@ export function formatWorkflowRun(run) {
1026
1037
  lines.push(`workflowId: ${run.workflowId}`);
1027
1038
  if (run.triggerRunId)
1028
1039
  lines.push(`triggerRunId: ${run.triggerRunId}`);
1029
- lines.push(`verdict: ${runStatusLabel(run.status)}${counts ? ` (${counts})` : ""}`);
1040
+ lines.push(`verdict: ${runVerdict(run)}${counts ? ` (${counts})` : ""}`);
1030
1041
  if (run.isTerminal)
1031
1042
  lines.push("terminal: yes");
1032
1043
  if (run.error)
@@ -1051,8 +1062,9 @@ export function formatWorkflowRun(run) {
1051
1062
  if (run.deliveries && run.deliveries.length > 0) {
1052
1063
  lines.push("");
1053
1064
  lines.push(`Deliveries (${run.deliveries.length}):`);
1054
- for (const delivery of run.deliveries)
1055
- lines.push(...deliveryLines(delivery));
1065
+ for (const delivery of run.deliveries) {
1066
+ lines.push(...deliveryLines(delivery, parkedNodeId));
1067
+ }
1056
1068
  }
1057
1069
  if (run.outputs && run.outputs.length > 0) {
1058
1070
  lines.push("");
@@ -1070,9 +1082,10 @@ export function formatWorkflowRun(run) {
1070
1082
  }
1071
1083
  return lines.join("\n");
1072
1084
  }
1085
+ const NOT_YET_APPROVED = "awaiting approval, not yet approved";
1073
1086
  function runOutputLines(output, awaitingReview = false) {
1074
1087
  const slug = output.botSlug ? ` (${output.botSlug})` : "";
1075
- const review = awaitingReview ? " — awaiting approval, not yet approved" : "";
1088
+ const review = awaitingReview ? ` — ${NOT_YET_APPROVED}` : "";
1076
1089
  if (output.type === "image") {
1077
1090
  return [` ${output.label} [image]${slug}: ${output.imageUrl ?? output.imageId ?? "(no url)"}${review}`];
1078
1091
  }
@@ -1102,14 +1115,19 @@ function runOutputLines(output, awaitingReview = false) {
1102
1115
  const note = normalized.length > 400 ? "\n (truncated — use --json for the full text)" : "";
1103
1116
  return [` ${output.label} [text]${slug}${review}:`, ` ${body}${note}`];
1104
1117
  }
1105
- function deliveryLines(delivery) {
1106
- const status = delivery.status === "unfulfilled" && delivery.error
1107
- ? `unfulfilled — ${delivery.error}`
1108
- : delivery.status;
1118
+ function deliveryLines(delivery, parkedNodeId) {
1119
+ const heldArtifact = (a) => parkedNodeId !== undefined && a.nodeId === parkedNodeId;
1120
+ const held = delivery.status === "delivered" && delivery.artifacts.some(heldArtifact);
1121
+ const status = held
1122
+ ? `held — ${NOT_YET_APPROVED}`
1123
+ : delivery.status === "unfulfilled" && delivery.error
1124
+ ? `unfulfilled — ${delivery.error}`
1125
+ : delivery.status;
1109
1126
  const lines = [` ${delivery.label} (${delivery.key}) · ${delivery.type} · ${status}`];
1110
1127
  for (const artifact of delivery.artifacts) {
1111
- for (const line of runOutputLines(artifact))
1128
+ for (const line of runOutputLines(artifact, heldArtifact(artifact))) {
1112
1129
  lines.push(` ${line}`);
1130
+ }
1113
1131
  }
1114
1132
  return lines;
1115
1133
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicopycoders/exodus",
3
- "version": "2026.8.1100",
3
+ "version": "2026.8.1200",
4
4
  "description": "CLI + Claude Code skills for the Copycoders Exodus ad pipeline",
5
5
  "repository": {
6
6
  "type": "git",