@aicopycoders/exodus 2026.6.2900-beta.7 → 2026.6.2900

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.
@@ -6,6 +6,7 @@ description: Spread finished ad copy across structured ad-type formats — testi
6
6
  ```operator-guide
7
7
  Subcommands:
8
8
  exodus template run --input "<text>" [options] Kick off a run
9
+ exodus template status --id <runId> Status + rendered image URLs
9
10
  exodus template resume --id <runId> Finalize an orphaned run (#56)
10
11
  exodus template ad-types Print all 50 AD_TYPES
11
12
  exodus template reptile-triggers Print the 13 reptile triggers
@@ -27,8 +28,9 @@ run options:
27
28
  (use `--quantities`).
28
29
 
29
30
  Returns:
30
- runId + dashboard URL. POST-only — there is NO CLI status endpoint yet.
31
- Live progress renders in the dashboard at /creative-suite/template/sessions/<runId>.
31
+ runId + dashboard URL. Kickoff is fire-and-forget; poll with
32
+ `exodus template status --id <runId>` for status + the rendered image URLs.
33
+ Live progress also renders in the dashboard at /creative-suite/template/sessions/<runId>.
32
34
  ```
33
35
 
34
36
  # Template — Ad-Type Format Variations (Fernando's pipeline)
@@ -76,11 +78,11 @@ With controls:
76
78
 
77
79
  **Cheap smoke test:** use `--render-mode prompts` to validate the format selection + prompts without burning render compute, then re-run with `--render-mode images` once the mix looks right.
78
80
 
79
- The command returns a `runId` + dashboard URL and exits — there is **no CLI status polling** for template (the kickoff route is POST-only). Renders land in the dashboard.
81
+ The command returns a `runId` + dashboard URL and exits — kickoff is fire-and-forget. Poll with `exodus template status --id <runId>` to read the status and pull the rendered image URLs once they complete.
80
82
 
81
83
  ### 3. Report
82
84
 
83
- Per the **Default Post-Run Reporting** rule in `exodus-strategist`: surface the dashboard URL + a 2-line take, then stop. Don't claim the images are done — the CLI only confirms the run *started*; the renders complete asynchronously in the dashboard. Tell the user where to watch: `/creative-suite/template/sessions/<runId>`.
85
+ Per the **Default Post-Run Reporting** rule in `exodus-strategist`: surface the dashboard URL + a 2-line take, then stop. Don't claim the images are done off the kickoff — that only confirms the run *started*; the renders complete asynchronously. Use `exodus template status --id <runId>` to confirm completion and retrieve the render URLs, or point the user to `/creative-suite/template/sessions/<runId>`.
84
86
 
85
87
  ### 4. Recover an orphaned run
86
88
 
@@ -95,12 +97,13 @@ If a previous run's renders stalled or were left incomplete, finalize it:
95
97
  - **`--mode manual` without `--quantities`** (or `hybrid` without `--pairings`) errors immediately — supply the required flag.
96
98
  - **Unknown ad-type slug** in `--quantities`/`--pairings` errors with the bad slug named. Run `exodus template ad-types` and use the exact slug.
97
99
  - **whoami failure** ("Check EXODUS_API_KEY…") means auth/base-URL isn't resolving — run `npx @aicopycoders/exodus doctor`.
98
- - **No CLI status** — if the user asks "is it done?", point them to the dashboard sessions URL; there is no `exodus status` for template yet. Don't fabricate a status.
100
+ - **Checking status** — if the user asks "is it done?", run `exodus template status --id <runId>`: it returns the run status and the rendered image URLs. Don't fabricate a status; read it.
99
101
  - **`--ref-image` is NOT supported in V1** — the Bearer kickoff route doesn't accept reference image ids. If the user wants reference matching, route to the `exodus-creative` skill's ref-match instead.
100
102
 
101
103
  ## Admin
102
104
 
103
105
  - Backend: Convex HTTP `POST /api/creative-suite-template/run` (Bearer-auth); Trigger.dev task `creative-suite-template` (Fernando's 5-stage pipeline).
104
106
  - 50 AD_TYPES and 13 reptile triggers are inspectable via `exodus template ad-types` / `exodus template reptile-triggers`.
107
+ - Status hits `GET /api/v2/template?runId=<id>` (status + rendered image URLs).
105
108
  - Resume hits `POST /api/creative-suite-template/runs/<id>/resume`.
106
109
  - Don't echo render prompts or bot system prompts in your reply — that's IP.
@@ -1,2 +1,11 @@
1
1
  export declare const helpText: string;
2
+ interface CreativeImage {
3
+ url: string;
4
+ source?: string;
5
+ cNumber?: string;
6
+ title?: string;
7
+ model?: string;
8
+ }
9
+ export declare function formatImageLines(images: CreativeImage[] | undefined): string[];
2
10
  export declare function run(flags: Record<string, string | boolean>): Promise<void>;
11
+ export {};
@@ -186,13 +186,28 @@ async function runStatus(flags) {
186
186
  if (d.completedImages !== undefined || d.totalImages !== undefined) {
187
187
  console.log(`progress: ${d.completedImages ?? 0} / ${d.totalImages ?? "?"} completed${d.failedImages ? `, ${d.failedImages} failed` : ""}`);
188
188
  }
189
- else if (d.imageCount !== undefined) {
189
+ else if (d.imageCount !== undefined && (!d.images || d.images.length === 0)) {
190
190
  console.log(`images: ${d.imageCount}`);
191
191
  }
192
192
  if (d.errorMessage)
193
193
  console.log(`error: ${d.errorMessage}`);
194
+ printImageUrls(d.images);
194
195
  console.log(`dashboard: ${getDashboardUrl()}/creative-suite/runs/${runId}`);
195
196
  }
197
+ export function formatImageLines(images) {
198
+ if (!images || images.length === 0)
199
+ return [];
200
+ const lines = [`images: ${images.length} (URLs below)`];
201
+ images.forEach((im, i) => {
202
+ const label = [im.cNumber, im.source].filter(Boolean).join(" · ");
203
+ lines.push(` ${String(i + 1).padStart(2)}. ${im.url}${label ? ` (${label})` : ""}`);
204
+ });
205
+ return lines;
206
+ }
207
+ function printImageUrls(images) {
208
+ for (const line of formatImageLines(images))
209
+ console.log(line);
210
+ }
196
211
  export async function run(flags) {
197
212
  const sub = process.argv[3] ?? "";
198
213
  if (sub === "native") {
@@ -1,2 +1,10 @@
1
1
  export declare const helpText: string;
2
+ interface TemplateRender {
3
+ url: string;
4
+ adType?: string;
5
+ status?: string;
6
+ model?: string;
7
+ }
8
+ export declare function formatRenderLines(renders: TemplateRender[] | undefined): string[];
2
9
  export declare function run(flags: Record<string, string | boolean>): Promise<void>;
10
+ export {};
@@ -6,6 +6,7 @@ exodus template — Fernando's Template pipeline (${AD_TYPES.length} AD_TYPES, r
6
6
 
7
7
  Usage:
8
8
  exodus template run --input "<ad brief or numbered ads>" [options]
9
+ exodus template status --id <runId>
9
10
  exodus template resume --id <runId>
10
11
  exodus template ad-types
11
12
  exodus template reptile-triggers
@@ -20,19 +21,24 @@ Run options:
20
21
  --realism off|realistic Realism enforcement (default: off)
21
22
  --quantities <slug:N,slug:N> Manual-mode per-type counts (e.g. "testimonial:3,hero:2")
22
23
  --requested-count N Auto-mode total render target (optional)
23
- --no-wait (default — no CLI-side polling endpoint yet)
24
+ --no-wait (default — kickoff is fire-and-forget; poll with status)
25
+
26
+ Status options:
27
+ --id <runId> Template run to read back (status + render URLs)
24
28
 
25
29
  Resume options:
26
30
  --id <runId> Template run id to resume (#56 orphan finalization)
27
31
 
28
- Status polling:
29
- The Convex HTTP route is POST-only. View live progress at the dashboard URL
30
- printed at kickoff (or the dashboard's /creative-suite/template/sessions list).
32
+ Status:
33
+ exodus template status --id <runId> prints the run status and the rendered
34
+ image URLs (GET /api/v2/template). Live progress is also on the dashboard URL
35
+ printed at kickoff.
31
36
 
32
37
  Examples:
33
38
  exodus template run --input "silver-threaded grounding sheets for inflammation"
34
39
  exodus template run --input "..." --aspect 9:16 --model nano-banana-pro --realism realistic
35
40
  exodus template run --input "..." --mode manual --quantities "testimonial:3,hero:2,ugc:4"
41
+ exodus template status --id <runId>
36
42
  exodus template resume --id <runId>
37
43
  exodus template ad-types
38
44
  `.trim();
@@ -167,7 +173,7 @@ async function runTemplate(flags) {
167
173
  console.log(` triggerRunId: ${triggerRunId}`);
168
174
  console.log(` dashboard: ${dashboardUrlForRun(runId)}`);
169
175
  console.log("");
170
- console.log("Status updates render live in the dashboard (no CLI status endpoint yet).");
176
+ console.log(`Poll status + pull render URLs: exodus template status --id ${runId}`);
171
177
  }
172
178
  async function runResume(flags) {
173
179
  const runId = typeof flags["id"] === "string" ? flags["id"] : undefined;
@@ -193,6 +199,42 @@ async function runResume(flags) {
193
199
  console.log(` triggerRunId: ${triggerRunId}`);
194
200
  console.log(` dashboard: ${dashboardUrlForRun(runId)}`);
195
201
  }
202
+ export function formatRenderLines(renders) {
203
+ if (!renders || renders.length === 0)
204
+ return [];
205
+ const lines = [`renders: ${renders.length} (URLs below)`];
206
+ renders.forEach((r, i) => {
207
+ lines.push(` ${String(i + 1).padStart(2)}. ${r.url}${r.adType ? ` (${r.adType})` : ""}`);
208
+ });
209
+ return lines;
210
+ }
211
+ async function runStatus(flags) {
212
+ const runId = typeof flags["id"] === "string" ? flags["id"] : undefined;
213
+ if (!runId) {
214
+ console.error("Error: template status requires --id <runId>.");
215
+ process.exit(1);
216
+ return;
217
+ }
218
+ const res = await apiGet(`/api/v2/template?runId=${encodeURIComponent(runId)}`);
219
+ if (!res.ok) {
220
+ console.error(formatError(res));
221
+ process.exit(1);
222
+ return;
223
+ }
224
+ const d = res.data;
225
+ console.log(`runId: ${d._id ?? runId}`);
226
+ if (d.name)
227
+ console.log(`name: ${d.name}`);
228
+ console.log(`status: ${d.status ?? "—"}`);
229
+ if (d.completedImageCount !== undefined || d.requestedImageCount !== undefined) {
230
+ console.log(`progress: ${d.completedImageCount ?? 0} / ${d.requestedImageCount ?? "?"} completed${d.failedImageCount ? `, ${d.failedImageCount} failed` : ""}`);
231
+ }
232
+ if (d.errorMessage)
233
+ console.log(`error: ${d.errorMessage}`);
234
+ for (const line of formatRenderLines(d.renders))
235
+ console.log(line);
236
+ console.log(`dashboard: ${dashboardUrlForRun(runId)}`);
237
+ }
196
238
  export async function run(flags) {
197
239
  const sub = process.argv[3] ?? "";
198
240
  if (sub === "ad-types") {
@@ -207,12 +249,16 @@ export async function run(flags) {
207
249
  await runResume(flags);
208
250
  return;
209
251
  }
252
+ if (sub === "status") {
253
+ await runStatus(flags);
254
+ return;
255
+ }
210
256
  if (sub === "run") {
211
257
  await runTemplate(flags);
212
258
  return;
213
259
  }
214
260
  if (!sub || sub.startsWith("--")) {
215
- console.error("Error: template requires a subcommand (run | resume | ad-types | reptile-triggers).");
261
+ console.error("Error: template requires a subcommand (run | resume | status | ad-types | reptile-triggers).");
216
262
  console.error("");
217
263
  console.error(helpText);
218
264
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicopycoders/exodus",
3
- "version": "2026.6.2900-beta.7",
3
+ "version": "2026.6.2900",
4
4
  "description": "CLI + Claude Code skills for the Copycoders Exodus ad pipeline",
5
5
  "repository": {
6
6
  "type": "git",