@aicopycoders/exodus 2026.7.200-beta.12 → 2026.7.200-beta.14

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.
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { apiGetDashboard, apiPostDashboard } from "../lib/client.js";
4
4
  import { promptChoice, promptMultiline, openInEditor } from "../lib/prompts.js";
5
+ import { normalizeRunStatus } from "../lib/runStatus.js";
5
6
  import { refreshBrandProfileMd } from "./brand.js";
6
7
  export const helpText = `
7
8
  exodus primer — Build a brand's primer (the single foundation step)
@@ -152,7 +153,7 @@ async function buildPrimer(submission) {
152
153
  process.stdout.write("·");
153
154
  continue;
154
155
  }
155
- if (data.status === "failed") {
156
+ if (normalizeRunStatus(data.status) === "failed") {
156
157
  console.log("");
157
158
  fail(`primer build failed: ${data.error ?? "unknown error"}`);
158
159
  }
@@ -1,5 +1,5 @@
1
1
  import { apiGet } from "../lib/client.js";
2
- import { formatGeneration, formatGenesisRun, formatError } from "../lib/format.js";
2
+ import { formatGeneration, formatGenesisRun, formatError, displayRunStatus } from "../lib/format.js";
3
3
  import { hydrateScoutIdeasCount } from "../lib/scout-hydrate.js";
4
4
  import { formatIntelResult as formatIntelResultRich } from "../lib/intel-format.js";
5
5
  export const helpText = `
@@ -31,7 +31,7 @@ export function formatIntelResult(data) {
31
31
  export function formatPulseResult(data) {
32
32
  const lines = [];
33
33
  lines.push("## Pulse Status");
34
- lines.push(`**Status:** ${data["status"] ?? "unknown"}`);
34
+ lines.push(`**Status:** ${displayRunStatus(data["status"])}`);
35
35
  if (data["accountId"])
36
36
  lines.push(`**Account ID:** ${data["accountId"]}`);
37
37
  if (data["commentsAnalyzed"] !== undefined)
@@ -61,7 +61,7 @@ export function formatPulseResult(data) {
61
61
  export function formatScoutResult(data) {
62
62
  const lines = [];
63
63
  lines.push("## Scout Status");
64
- lines.push(`**Status:** ${data["status"] ?? "unknown"}`);
64
+ lines.push(`**Status:** ${displayRunStatus(data["status"])}`);
65
65
  const pipelineSlug = data["pipelineSlug"];
66
66
  if (pipelineSlug)
67
67
  lines.push(`**Pipeline:** ${pipelineSlug}`);
@@ -123,7 +123,7 @@ export function formatScoutResult(data) {
123
123
  export function formatCreativeSuiteStatus(title, data) {
124
124
  const lines = [];
125
125
  lines.push(`## ${title}`);
126
- lines.push(`**Status:** ${data["status"] ?? "unknown"}`);
126
+ lines.push(`**Status:** ${displayRunStatus(data["status"])}`);
127
127
  const show = (label, key) => {
128
128
  const v = data[key];
129
129
  if (v !== undefined && v !== null && v !== "")
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import { apiGet, apiPost } from "../lib/client.js";
3
- import { formatError } from "../lib/format.js";
3
+ import { displayRunStatus, formatError } from "../lib/format.js";
4
4
  export const helpText = `
5
5
  exodus swipe — Swipe pipeline lifecycle + competitor watchlist
6
6
 
@@ -121,7 +121,7 @@ async function runRunPipeline(flags) {
121
121
  return;
122
122
  }
123
123
  console.log(`Run started: ${res.data.runId}`);
124
- console.log(` status: ${res.data.status ?? "selecting"}`);
124
+ console.log(` status: ${displayRunStatus(res.data.status, "Running")}`);
125
125
  if (res.data.triggerRunId)
126
126
  console.log(` triggerRunId: ${res.data.triggerRunId}`);
127
127
  console.log(`\nPoll: exodus swipe status ${res.data.runId}`);
@@ -224,13 +224,13 @@ async function runHistory(flags) {
224
224
  for (const r of runs) {
225
225
  const when = r.startedAt ? new Date(r.startedAt).toLocaleString() : "?";
226
226
  const doc = r.docUrl ? ` doc=${r.docUrl}` : r.error ? ` error=${r.error}` : "";
227
- console.log(` ${r.runId} ${r.status} ads=${r.adsSelectedCount} by=${r.triggeredBy} ${when}${doc}`);
227
+ console.log(` ${r.runId} ${displayRunStatus(r.status, "—")} ads=${r.adsSelectedCount} by=${r.triggeredBy} ${when}${doc}`);
228
228
  }
229
229
  }
230
230
  function printRunStatus(r) {
231
231
  console.log(`Run ${r.runId ?? "?"}`);
232
232
  console.log(` brand: ${r.targetBrandSlug ?? "?"}`);
233
- console.log(` status: ${r.status ?? "?"}`);
233
+ console.log(` status: ${displayRunStatus(r.status, "?")}`);
234
234
  console.log(` by: ${r.triggeredBy ?? "?"}`);
235
235
  console.log(` ads: ${r.adsSelectedCount ?? 0}`);
236
236
  if (r.startedAt)
@@ -1,5 +1,5 @@
1
1
  import { apiGet, apiPost, apiPostDashboard, getDashboardUrl } from "../lib/client.js";
2
- import { formatError } from "../lib/format.js";
2
+ import { displayRunStatus, formatError } from "../lib/format.js";
3
3
  import { AD_TYPES, AD_TYPE_NAMES, REPTILE_TRIGGERS, } from "../lib/template-constants.js";
4
4
  export const helpText = `
5
5
  exodus template — Fernando's Template pipeline (${AD_TYPES.length} AD_TYPES, resume)
@@ -225,7 +225,7 @@ async function runStatus(flags) {
225
225
  console.log(`runId: ${d._id ?? runId}`);
226
226
  if (d.name)
227
227
  console.log(`name: ${d.name}`);
228
- console.log(`status: ${d.status ?? "—"}`);
228
+ console.log(`status: ${displayRunStatus(d.status, "—")}`);
229
229
  if (d.completedImageCount !== undefined || d.requestedImageCount !== undefined) {
230
230
  console.log(`progress: ${d.completedImageCount ?? 0} / ${d.requestedImageCount ?? "?"} completed${d.failedImageCount ? `, ${d.failedImageCount} failed` : ""}`);
231
231
  }
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { apiGet, apiPost } from "../lib/client.js";
4
- import { formatError } from "../lib/format.js";
4
+ import { displayRunStatus, formatError, tickerRunStatus } from "../lib/format.js";
5
5
  import { pollUntilDone } from "../lib/poll.js";
6
6
  export const helpText = `
7
7
  exodus winners — Import your own brand's winning ads as generative fuel
@@ -290,7 +290,7 @@ async function runImport(positional, flags) {
290
290
  const status = typeof data.status === "string" ? data.status : "";
291
291
  if (!json && status && status !== lastStatus) {
292
292
  lastStatus = status;
293
- console.log(` status: ${status}`);
293
+ console.log(` status: ${tickerRunStatus(status)}`);
294
294
  }
295
295
  },
296
296
  });
@@ -343,7 +343,7 @@ async function uploadAsset(absPath, json) {
343
343
  }
344
344
  function printOutcomeTable(data) {
345
345
  const winners = data.winners ?? [];
346
- console.log(`\nImport ${data.importId ?? "?"}: ${data.status ?? "?"}`);
346
+ console.log(`\nImport ${data.importId ?? "?"}: ${displayRunStatus(data.status, "?")}`);
347
347
  if (data.error)
348
348
  console.log(` error: ${data.error}`);
349
349
  const created = winners.filter((w) => w.created === true).length;
@@ -1,8 +1,9 @@
1
1
  import { type ApiResponse } from "../lib/client.js";
2
2
  import { type PollOptions, type PollResult } from "../lib/poll.js";
3
+ import { LEGACY_WORKFLOW_RUN_STATUS_VALUES, type RunStatus } from "../lib/runStatus.js";
3
4
  import { type Channel } from "../lib/channel.js";
4
5
  export declare const helpText: string;
5
- export type WorkflowNodeKind = "brief" | "bot" | "primer" | "image" | "rig" | "storyboard" | "reference" | "scene-frames" | "video" | "voiceover" | "output" | "push" | "gate" | "call" | "show-set" | "show-cast" | "show-voices" | "product-truth" | "transform";
6
+ export type WorkflowNodeKind = "brief" | "asset" | "bot" | "primer" | "image" | "image-rig" | "rig" | "storyboard" | "reference" | "scene-frames" | "video" | "voiceover" | "output" | "call" | "show-set" | "show-cast" | "show-voices" | "product-truth" | "transform" | "formatter" | "splitter" | "collector";
6
7
  export type WorkflowSlotState = "locked" | "auto" | "ask" | "inferred";
7
8
  export interface WorkflowSlot {
8
9
  id: string;
@@ -63,10 +64,11 @@ export interface WorkflowImportResult {
63
64
  workflowId?: string;
64
65
  nodeCount: number;
65
66
  edgeCount: number;
67
+ triggers?: WorkflowTrigger[];
66
68
  unresolved: UnresolvedWorkflowRef[];
67
69
  warnings: string[];
68
70
  }
69
- export type GraphIssueCode = "bad-shape" | "unknown-kind" | "duplicate-node-id" | "dangling-edge" | "unknown-port" | "type-mismatch" | "duplicate-input" | "session-fan-out" | "cycle" | "missing-required-input" | "bad-config" | "bad-slot" | "bad-trigger";
71
+ export type GraphIssueCode = "bad-shape" | "unknown-kind" | "duplicate-node-id" | "dangling-edge" | "unknown-port" | "type-mismatch" | "duplicate-input" | "duplicate-input-key" | "cycle" | "missing-required-input" | "bad-config" | "bad-slot" | "bad-trigger" | "nested-split" | "fan-overlap" | "lane-ineligible" | "collector-unpaired";
70
72
  export interface GraphIssue {
71
73
  code: GraphIssueCode;
72
74
  message: string;
@@ -82,7 +84,7 @@ export interface WorkflowImportError {
82
84
  issues?: GraphIssue[];
83
85
  currentUpdatedAt?: string;
84
86
  }
85
- export type WorkflowPortType = "text" | "primer" | "image" | "rig" | "storyboard" | "frames" | "video" | "audio" | "show" | "session";
87
+ export type WorkflowPortType = "text" | "primer" | "image" | "rig" | "storyboard" | "frames" | "video" | "audio" | "document" | "show" | "session";
86
88
  export type WorkflowPrimerKind = "body" | "hook" | "headline" | "summary";
87
89
  export type WorkflowDurationSpec = {
88
90
  kind: "fixed";
@@ -162,20 +164,25 @@ export interface WorkflowCatalog {
162
164
  };
163
165
  }
164
166
  export type WorkflowBriefSource = "text" | "swipe-ad" | "swipe-bundle" | "organic-url" | "ad-url";
167
+ export type WorkflowMediaType = "image" | "video" | "audio" | "document";
168
+ export type WorkflowEntryFieldType = "text" | "select" | "number" | "toggle";
165
169
  export interface WorkflowInputDescriptor {
166
170
  fieldName: string;
167
171
  nodeId: string;
168
- source: WorkflowBriefSource;
172
+ source: WorkflowBriefSource | "asset";
169
173
  required: boolean;
170
174
  description?: string;
175
+ type?: WorkflowEntryFieldType;
176
+ options?: string[];
171
177
  bundleSize?: number;
178
+ assetType?: WorkflowMediaType;
172
179
  }
173
180
  export interface WorkflowPrerequisiteDescriptor {
174
181
  primerKind: WorkflowPrimerKind;
175
182
  nodeIds: string[];
176
183
  }
177
184
  export interface WorkflowOutputDescriptor {
178
- type: "text" | "image" | "video" | "audio" | "frames" | "storyboard";
185
+ type: "text" | "image" | "video" | "audio" | "document" | "frames" | "storyboard";
179
186
  label: string;
180
187
  nodeId: string;
181
188
  botSlug?: string;
@@ -224,27 +231,40 @@ export interface WorkflowVersion {
224
231
  export interface WorkflowVersionsResponse {
225
232
  versions: WorkflowVersion[];
226
233
  }
227
- export type WorkflowRunStatus = "queued" | "running" | "awaiting-review" | "completed" | "partial" | "failed" | "canceled";
234
+ export type WorkflowRunStatus = RunStatus | (typeof LEGACY_WORKFLOW_RUN_STATUS_VALUES)[number];
228
235
  export type WorkflowNodeRunStatus = "idle" | "running" | "done" | "failed" | "skipped";
236
+ export interface ArtifactItemIdentity {
237
+ index: number;
238
+ total: number;
239
+ }
229
240
  export type WorkflowArtifact = {
230
241
  type: "text";
231
242
  text: string;
232
243
  label?: string;
233
244
  port?: string;
234
245
  humanEdited?: boolean;
246
+ item?: ArtifactItemIdentity;
235
247
  } | {
236
248
  type: "primer";
237
249
  text: string;
238
250
  primerKind: string;
251
+ item?: ArtifactItemIdentity;
239
252
  } | {
240
253
  type: "image";
241
254
  storageId: string;
242
255
  imageUrl?: string;
256
+ item?: ArtifactItemIdentity;
257
+ } | {
258
+ type: "session";
259
+ sessionId: string;
260
+ label?: string;
261
+ port?: string;
262
+ item?: ArtifactItemIdentity;
243
263
  };
244
264
  export interface WorkflowRunOutput {
245
265
  nodeId: string;
246
266
  botSlug?: string;
247
- type: "text" | "image" | "video" | "audio" | "frames" | "storyboard";
267
+ type: "text" | "image" | "video" | "audio" | "document" | "frames" | "storyboard";
248
268
  label: string;
249
269
  text?: string;
250
270
  imageUrl?: string;
@@ -259,10 +279,26 @@ export interface WorkflowRunOutput {
259
279
  imageUrl?: string;
260
280
  }>;
261
281
  storyboardJson?: string;
282
+ documentUrl?: string;
283
+ filename?: string;
284
+ mimeType?: string;
285
+ }
286
+ export type WorkflowOutputSlotType = "text" | "structured" | "asset" | "collection";
287
+ export interface WorkflowRunDelivery {
288
+ key: string;
289
+ label: string;
290
+ type: WorkflowOutputSlotType;
291
+ description?: string;
292
+ order: number;
293
+ nodeId: string;
294
+ status: "delivered" | "unfulfilled";
295
+ error?: string;
296
+ artifacts: WorkflowRunOutput[];
262
297
  }
263
298
  export interface WorkflowRunNode {
264
299
  nodeId: string;
265
300
  kind: string;
301
+ botSlug?: string;
266
302
  status: WorkflowNodeRunStatus;
267
303
  error?: string;
268
304
  startedAt?: number;
@@ -281,7 +317,7 @@ export interface WorkflowRunSession {
281
317
  title: string;
282
318
  botSlug: string;
283
319
  }
284
- export type WorkflowPauseReason = "taste" | "repair" | "slots" | "call";
320
+ export type WorkflowPauseReason = "taste" | "repair" | "slots" | "call" | "checkpoint";
285
321
  export interface WorkflowPendingSlot {
286
322
  id: string;
287
323
  label?: string;
@@ -306,6 +342,7 @@ export interface WorkflowRun {
306
342
  isTerminal: boolean;
307
343
  nodes: WorkflowRunNode[];
308
344
  outputs?: WorkflowRunOutput[];
345
+ deliveries?: WorkflowRunDelivery[];
309
346
  sessions?: WorkflowRunSession[];
310
347
  pauseReason?: WorkflowPauseReason;
311
348
  pausedNodeId?: string;
@@ -321,10 +358,22 @@ export interface WorkflowRunDeps {
321
358
  readFile: (path: string) => string;
322
359
  writeFile: (path: string, text: string) => void;
323
360
  poll: (opts: PollOptions) => Promise<PollResult>;
361
+ mkdirp?: (dir: string) => void;
362
+ downloadToFile?: (url: string, path: string) => Promise<void>;
324
363
  postDashboard?: (path: string, body: unknown, opts?: {
325
364
  timeoutMs?: number;
326
365
  }) => Promise<ApiResponse<unknown>>;
327
366
  dashboardUrl?: string;
367
+ statFile?: (filePath: string) => {
368
+ size: number;
369
+ } | null;
370
+ readFileBytes?: (filePath: string) => Uint8Array;
371
+ uploadBytes?: (uploadUrl: string, contentType: string, bytes: Uint8Array) => Promise<{
372
+ ok: boolean;
373
+ status: number;
374
+ storageId?: string;
375
+ body?: string;
376
+ }>;
328
377
  }
329
378
  export interface FlowResult {
330
379
  code: number;
@@ -333,13 +382,22 @@ export interface FlowResult {
333
382
  interface RunFlowOptions {
334
383
  inputs: Record<string, string>;
335
384
  terminalNodeIds?: string[];
385
+ fill?: string;
336
386
  wait: boolean;
337
387
  json: boolean;
388
+ out?: string;
338
389
  onProgressLine?: (line: string) => void;
339
390
  }
340
391
  export declare function formatPauseNotice(pauseReason: WorkflowPauseReason | undefined, runId: string, dashboardUrl: string): string[];
392
+ export declare function parseRawInputFlags(args: string[]): Record<string, string>;
341
393
  export declare function parseInputFlags(args: string[], readFile?: (path: string) => string): Record<string, string>;
394
+ export declare const ASSET_UPLOAD_POLICY: Record<WorkflowMediaType, {
395
+ mimeByExtension: Record<string, string>;
396
+ maxBytes: number;
397
+ accepts: string;
398
+ }>;
342
399
  export declare function parseTerminalFlags(args: string[]): string[];
400
+ export declare function parseFillFlag(args: string[]): string | undefined;
343
401
  export declare function formatWorkflowList(workflows: WorkflowListItem[]): string;
344
402
  export declare function formatRecentRuns(runs: WorkflowRunProjection[]): string;
345
403
  export declare function formatWorkflowVersions(versions: WorkflowVersion[]): string;
@@ -349,6 +407,12 @@ export declare function formatImportSummary(result: WorkflowImportResult, mode?:
349
407
  validate?: boolean;
350
408
  }): string;
351
409
  export declare function formatWorkflowRun(run: WorkflowRun): string;
410
+ export interface DeliverySaveResult {
411
+ lines: string[];
412
+ paths: string[];
413
+ }
414
+ export declare function workflowFilenameSlug(name: string): string;
415
+ export declare function saveDeliveries(run: WorkflowRun, dir: string, deps: WorkflowRunDeps): Promise<DeliverySaveResult>;
352
416
  export declare function formatDescribe(res: WorkflowDescribeResponse): string;
353
417
  export declare function formatBotsList(catalog: WorkflowCatalog, category?: string): string;
354
418
  export declare function formatBotDetail(bot: CatalogBot): string;
@@ -368,6 +432,7 @@ export declare function runFlow(workflowRef: string, opts: RunFlowOptions, deps:
368
432
  export declare function statusFlow(opts: {
369
433
  id?: string;
370
434
  json: boolean;
435
+ out?: string;
371
436
  }, deps: WorkflowRunDeps): Promise<FlowResult>;
372
437
  export declare function exportFlow(workflowRef: string, opts: {
373
438
  out?: string;
@@ -422,7 +487,7 @@ export interface WorkflowInboxRow {
422
487
  workflowName: string;
423
488
  pausedNodeId?: string;
424
489
  pausedNodeKind?: string;
425
- pauseReason?: "taste" | "repair" | "slots";
490
+ pauseReason?: "taste" | "repair" | "slots" | "checkpoint";
426
491
  counts?: WorkflowCounts;
427
492
  createdAt: number | string;
428
493
  queuedAt?: number | string;
@@ -442,28 +507,35 @@ export declare function parkBadge(pauseReason: string | undefined): string;
442
507
  export declare function invocationBadge(row: WorkflowInboxRow): string;
443
508
  export declare function formatInbox(rows: WorkflowInboxRow[], now?: number): string;
444
509
  export declare function inboxFlow(json: boolean, deps: WorkflowRunDeps): Promise<FlowResult>;
445
- export declare function gateShowFlow(runId: string, opts: {
510
+ export declare function checkpointShowFlow(runId: string, opts: {
446
511
  json: boolean;
447
512
  }, deps: WorkflowRunDeps): Promise<FlowResult>;
448
- export declare function gatePickFlow(runId: string, numbers: number[], opts: {
513
+ export declare function parseRejectItems(raw: string | undefined): {
514
+ ok: true;
515
+ items: number[];
516
+ } | {
517
+ ok: false;
518
+ message: string;
519
+ };
520
+ export declare function checkpointApproveFlow(runId: string, opts: {
521
+ wait: boolean;
449
522
  json: boolean;
523
+ onProgressLine?: (line: string) => void;
524
+ reject?: string;
450
525
  }, deps: WorkflowRunDeps): Promise<FlowResult>;
451
- export declare function gateEditFlow(runId: string, n: number, sources: {
526
+ export declare function checkpointEditFlow(runId: string, n: number, sources: {
452
527
  text?: string;
453
528
  file?: string;
454
529
  stdin?: string;
455
530
  }, opts: {
456
531
  json: boolean;
457
532
  }, deps: WorkflowRunDeps): Promise<FlowResult>;
458
- export declare function gatePushFlow(runId: string, message: string, opts: {
459
- json: boolean;
460
- }, deps: WorkflowRunDeps): Promise<FlowResult>;
461
- export declare function gateApproveFlow(runId: string, opts: {
533
+ export declare function checkpointRetryFlow(runId: string, opts: {
462
534
  wait: boolean;
463
535
  json: boolean;
464
536
  onProgressLine?: (line: string) => void;
465
537
  }, deps: WorkflowRunDeps): Promise<FlowResult>;
466
- export declare function gateRejectFlow(runId: string, opts: {
538
+ export declare function checkpointCancelFlow(runId: string, opts: {
467
539
  reason?: string;
468
540
  json: boolean;
469
541
  }, deps: WorkflowRunDeps): Promise<FlowResult>;