@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.
|
|
31
|
-
|
|
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 —
|
|
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
|
|
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
|
-
- **
|
|
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 —
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
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(
|
|
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);
|