@aicopycoders/exodus 2026.9.2000-beta.2 → 2026.9.2000-beta.20
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.
- package/assets/docs/PIPELINES.md +2 -1
- package/assets/skills/exodus-meme/SKILL.md +4 -3
- package/assets/skills/exodus-template/SKILL.md +2 -1
- package/assets/skills/exodus-video/SKILL.md +165 -27
- package/assets/skills/exodus-video/scripts/first-cut.mjs +538 -38
- package/assets/skills/exodus-workflow/SKILL.md +1 -1
- package/dist/commands/ads.js +1 -1
- package/dist/commands/image.js +1 -1
- package/dist/commands/template.js +11 -4
- package/dist/commands/video.d.ts +126 -7
- package/dist/commands/video.js +686 -58
- package/dist/commands/workflow.d.ts +52 -4
- package/dist/commands/workflow.js +60 -8
- package/dist/lib/helperLedgerBase.d.ts +3 -0
- package/dist/lib/helperLedgerBase.js +3 -0
- package/dist/lib/meta-ads.d.ts +1 -0
- package/package.json +1 -1
|
@@ -161,7 +161,7 @@ Accepts a workflow id **or** a name. It reports three things:
|
|
|
161
161
|
|
|
162
162
|
Fill every required input. For a free-text brief you can compose a **bespoke brief from in-session research** — the winning angle you just worked out, the swipe you analyzed — and pass it inline (`--input brief="..."`) or, if it's long, write it to a project file and pass it with `@file` (see Chain, below). For URL/swipe sources, pass the value the source expects.
|
|
163
163
|
|
|
164
|
-
**A brief wired straight into a Storyboard is a script, not a prompt.** On a video workflow whose Storyboard takes its `script` port from a Brief node, the text you pass for that brief boards VERBATIM, the same way a Show ad's pasted script does: one speaker per scene, every line rendered once in order, long lines split across clips, narrated lines rendered silent under the voice track. So it must be in the script form the `exodus-video` skill documents (an optional `CAST:` block of `NAME — description` lines, then `NAME:` turns, then a `CTA:` block; `NARRATOR:` lines only for Narrated Story and Explainer). A brief that is not a script, or one that does not fit the wired rig's family, is refused at launch with the parser's own message and nothing is spent. Write the script to a file and pass it with `--input script=@script.txt`. A Storyboard fed by a bot is unaffected.
|
|
164
|
+
**A brief wired straight into a Storyboard is a script, not a prompt.** On a video workflow whose Storyboard takes its `script` port from a Brief node, the text you pass for that brief boards VERBATIM, the same way a Show ad's pasted script does: one speaker per scene, every line rendered once in order, long lines split across clips, narrated lines rendered silent under the voice track. So it must be in the script form the `exodus-video` skill documents (an optional `DIRECTION:` block with no colon in its prose, then an optional `CAST:` block of `NAME — description` lines, then `NAME:` turns, then a `CTA:` block; `NARRATOR:` lines only for Narrated Story and Explainer). A brief that is not a script, or one that does not fit the wired rig's family, is refused at launch with the parser's own message and nothing is spent. Write the script to a file and pass it with `--input script=@script.txt`. A Storyboard fed by a bot is unaffected.
|
|
165
165
|
|
|
166
166
|
**A saved rig can bring a rulebook with it.** Some saved rigs carry saved format rules: how many speakers the script may have, which checks run on the clips, how the cast is built. Point a Rig box at one of those rigs and the run follows its rules — an ensemble rig seats more speakers than the usual three, a talking-head rig seats one. The start receipt says which rulebook ran:
|
|
167
167
|
|
package/dist/commands/ads.js
CHANGED
|
@@ -268,7 +268,7 @@ function windowBlock(label, stats) {
|
|
|
268
268
|
` spend: ${spendCell(stats)}`,
|
|
269
269
|
` ctr: ${ctrCell(stats)}`,
|
|
270
270
|
` cpc: ${cpcCell(stats)}`,
|
|
271
|
-
` impressions: ${count(stats.impressions)} · clicks ${count(stats.clicks)}`,
|
|
271
|
+
` impressions: ${count(stats.impressions)} · reach ${count(stats.reach)} · clicks ${count(stats.clicks)}`,
|
|
272
272
|
];
|
|
273
273
|
}
|
|
274
274
|
function copyBlock(label, text) {
|
package/dist/commands/image.js
CHANGED
|
@@ -184,7 +184,7 @@ async function runTemplate(text, flags, who) {
|
|
|
184
184
|
renderMode: "images",
|
|
185
185
|
inputAds: text,
|
|
186
186
|
aspectRatio: aspect,
|
|
187
|
-
model: "gpt-image-2",
|
|
187
|
+
model: "gpt-image-2.5",
|
|
188
188
|
realismMode: realisticFlag ? "realistic" : "off",
|
|
189
189
|
...(steering ? { steering } : {}),
|
|
190
190
|
...(requestedImageCount !== undefined ? { requestedImageCount } : {}),
|
|
@@ -17,7 +17,8 @@ Run options:
|
|
|
17
17
|
--mode auto|manual Generation mode (default: auto)
|
|
18
18
|
--render-mode images|prompts Skip render and return prompts only (default: images)
|
|
19
19
|
--aspect 1:1|9:16 Image aspect ratio (default: 1:1)
|
|
20
|
-
--model gpt-image-2|
|
|
20
|
+
--model gpt-image-2.5|gpt-image-2.5-sunburst|gpt-image-2|nano-banana-pro
|
|
21
|
+
Kie.ai model (default: gpt-image-2.5)
|
|
21
22
|
--realism off|realistic Realism enforcement (default: off)
|
|
22
23
|
--quantities <slug:N,slug:N> Manual-mode per-type counts (e.g. "testimonial:3,hero:2")
|
|
23
24
|
--requested-count N Auto-mode total render target (optional)
|
|
@@ -103,9 +104,15 @@ async function runTemplate(flags) {
|
|
|
103
104
|
process.exit(1);
|
|
104
105
|
return;
|
|
105
106
|
}
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
const TEMPLATE_MODELS = [
|
|
108
|
+
"gpt-image-2.5",
|
|
109
|
+
"gpt-image-2.5-sunburst",
|
|
110
|
+
"gpt-image-2",
|
|
111
|
+
"nano-banana-pro",
|
|
112
|
+
];
|
|
113
|
+
const model = typeof flags["model"] === "string" ? flags["model"] : "gpt-image-2.5";
|
|
114
|
+
if (!TEMPLATE_MODELS.includes(model)) {
|
|
115
|
+
console.error(`Error: --model must be one of ${TEMPLATE_MODELS.join(" | ")} (got "${model}").`);
|
|
109
116
|
process.exit(1);
|
|
110
117
|
return;
|
|
111
118
|
}
|
package/dist/commands/video.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { type ApiResponse } from "../lib/client.js";
|
|
2
2
|
import type { FlagOccurrence } from "../lib/args.js";
|
|
3
3
|
import { type RunProvenance, type WorkflowRun } from "./workflow.js";
|
|
4
|
+
declare const CONCEIT_KEYS: readonly ["podcast", "ugc", "stage", "street", "personification"];
|
|
5
|
+
declare const VOICE_MODES: readonly ["native", "voice-first"];
|
|
6
|
+
type VoiceMode = (typeof VOICE_MODES)[number];
|
|
4
7
|
export declare const helpText: string;
|
|
5
8
|
export declare const VOICES_PATH = "/api/v2/video/voices";
|
|
6
9
|
export interface ClipWord {
|
|
@@ -8,10 +11,32 @@ export interface ClipWord {
|
|
|
8
11
|
s: number;
|
|
9
12
|
e: number;
|
|
10
13
|
}
|
|
14
|
+
export type ClipQcTakeNeighbours = {
|
|
15
|
+
state: "not-applicable";
|
|
16
|
+
} | {
|
|
17
|
+
state: "not-attached";
|
|
18
|
+
} | {
|
|
19
|
+
state: "none-accepted";
|
|
20
|
+
} | {
|
|
21
|
+
state: "attached";
|
|
22
|
+
scenes: number[];
|
|
23
|
+
};
|
|
24
|
+
export interface ClipQcTake {
|
|
25
|
+
kind: "take" | "soft-retry";
|
|
26
|
+
failCodes: string[];
|
|
27
|
+
warnCodes: string[];
|
|
28
|
+
neighbours: ClipQcTakeNeighbours;
|
|
29
|
+
judgeWording?: {
|
|
30
|
+
code: string;
|
|
31
|
+
wording: string;
|
|
32
|
+
}[];
|
|
33
|
+
heardText?: string;
|
|
34
|
+
}
|
|
11
35
|
export interface ClipQc {
|
|
12
36
|
verdict: "pass" | "fail";
|
|
13
37
|
attempts: number;
|
|
14
38
|
neighbours?: number[];
|
|
39
|
+
takes?: ClipQcTake[];
|
|
15
40
|
}
|
|
16
41
|
export interface ClipFinding {
|
|
17
42
|
check: string;
|
|
@@ -21,6 +46,9 @@ export interface ClipFinding {
|
|
|
21
46
|
judgeDetail?: string;
|
|
22
47
|
judgeSeverity?: "fail" | "warn";
|
|
23
48
|
}
|
|
49
|
+
export declare function qcWithoutJudgeWording(qc: ClipQc): ClipQc;
|
|
50
|
+
export declare const NO_TAKE_HISTORY_LINE = "no history recorded for this clip";
|
|
51
|
+
export declare function renderQcTakeHistory(takes: ClipQcTake[] | undefined): string[];
|
|
24
52
|
export type ArtifactSubset = {
|
|
25
53
|
type: "storyboard";
|
|
26
54
|
storyboard?: unknown;
|
|
@@ -47,6 +75,8 @@ export type ArtifactSubset = {
|
|
|
47
75
|
speechTrimmed?: boolean;
|
|
48
76
|
tailTrimmed?: boolean;
|
|
49
77
|
rawStorageId?: string;
|
|
78
|
+
voiceMode?: "voice-first";
|
|
79
|
+
voiceFirstBlockedBy?: string[];
|
|
50
80
|
} | {
|
|
51
81
|
type: "audio";
|
|
52
82
|
sceneIndex?: number;
|
|
@@ -69,13 +99,29 @@ export type ArtifactSubset = {
|
|
|
69
99
|
} | {
|
|
70
100
|
type: "text" | "primer" | "session" | "document";
|
|
71
101
|
};
|
|
102
|
+
export interface PlanFailureRecord {
|
|
103
|
+
kind: string;
|
|
104
|
+
partsTotal?: number;
|
|
105
|
+
acceptedParts?: number;
|
|
106
|
+
acceptedScenes?: number;
|
|
107
|
+
failedPart?: number;
|
|
108
|
+
reasons?: string[];
|
|
109
|
+
lines: string[];
|
|
110
|
+
}
|
|
72
111
|
export interface VideoRunNode {
|
|
73
112
|
nodeId: string;
|
|
74
113
|
kind: string;
|
|
75
|
-
status: "idle" | "running" | "done" | "failed" | "skipped";
|
|
114
|
+
status: "idle" | "running" | "done" | "failed" | "skipped" | "out-of-scope";
|
|
76
115
|
error?: string;
|
|
77
116
|
warning?: string;
|
|
117
|
+
planFailure?: PlanFailureRecord;
|
|
118
|
+
hasRejectedDraft?: boolean;
|
|
78
119
|
outputs?: ArtifactSubset[];
|
|
120
|
+
missingScenes?: MissingScene[];
|
|
121
|
+
}
|
|
122
|
+
export interface MissingScene {
|
|
123
|
+
sceneIndex: number;
|
|
124
|
+
pictureRefused: boolean;
|
|
79
125
|
}
|
|
80
126
|
export type BuilderPauseReason = "taste" | "repair" | "slots" | "call" | "checkpoint";
|
|
81
127
|
export interface PullCastLockMember {
|
|
@@ -103,6 +149,8 @@ export interface VideoRun {
|
|
|
103
149
|
pauseAhead?: WorkflowRun["pauseAhead"];
|
|
104
150
|
castLock?: PullCastLock | null;
|
|
105
151
|
provenance?: RunProvenance;
|
|
152
|
+
musicBed?: boolean;
|
|
153
|
+
videoChoice?: RunVideoChoice;
|
|
106
154
|
workflowId?: string;
|
|
107
155
|
moduleOwned?: boolean;
|
|
108
156
|
}
|
|
@@ -121,6 +169,11 @@ export interface NodeItem {
|
|
|
121
169
|
take: number;
|
|
122
170
|
outcome: string;
|
|
123
171
|
label: string;
|
|
172
|
+
reason?: string;
|
|
173
|
+
displacedHistory?: {
|
|
174
|
+
title: string;
|
|
175
|
+
lines: string[];
|
|
176
|
+
};
|
|
124
177
|
};
|
|
125
178
|
artifact?: ArtifactSubset;
|
|
126
179
|
}
|
|
@@ -173,8 +226,11 @@ export type RunStop = {
|
|
|
173
226
|
at: "storyboard-gate";
|
|
174
227
|
nodeId?: string;
|
|
175
228
|
framesNodeId?: string;
|
|
229
|
+
showAd?: true;
|
|
176
230
|
} | {
|
|
177
231
|
at: "final-watch";
|
|
232
|
+
missingScenes?: MissingScene[];
|
|
233
|
+
framesNodeId?: string;
|
|
178
234
|
} | {
|
|
179
235
|
at: "paused";
|
|
180
236
|
nodeId?: string;
|
|
@@ -191,18 +247,23 @@ export type RunStop = {
|
|
|
191
247
|
};
|
|
192
248
|
export type ResolvedStop = Exclude<RunStop, {
|
|
193
249
|
at: "final-watch";
|
|
194
|
-
}> | {
|
|
250
|
+
}> | (Extract<RunStop, {
|
|
195
251
|
at: "final-watch";
|
|
252
|
+
}> & {
|
|
196
253
|
cutAttached: boolean | null;
|
|
197
|
-
};
|
|
254
|
+
});
|
|
198
255
|
export declare function classifyRun(run: VideoRun): RunStop;
|
|
199
256
|
export declare function hasAttachedCut(items: NodeItem[]): boolean;
|
|
200
257
|
export declare function resolveStop(stop: RunStop, cutAttached: boolean | null): ResolvedStop;
|
|
201
258
|
export declare function resolveStopAtPark(stop: RunStop, runId: string, deps: Pick<VideoDeps, "get">): Promise<ResolvedStop>;
|
|
202
259
|
export declare function stageWord(stage: string): string;
|
|
203
260
|
export declare function stepName(kind: string | undefined): string;
|
|
204
|
-
export declare function
|
|
261
|
+
export declare function isShowAd(run: Pick<VideoRun, "moduleOwned">): boolean;
|
|
262
|
+
export declare function reviewUrl(dashboardUrl: string, run: Pick<VideoRun, "_id" | "workflowId">): string;
|
|
205
263
|
export declare function stopLines(stop: ResolvedStop, runId: string, runUrl: string): string[];
|
|
264
|
+
export declare function failedStoryboardNode(run: VideoRun): VideoRunNode | undefined;
|
|
265
|
+
export declare function errorEchoesReasons(error: string | undefined, record: PlanFailureRecord | undefined): boolean;
|
|
266
|
+
export declare function planFailureLines(node: VideoRunNode | undefined, runId: string): string[];
|
|
206
267
|
export interface PullDownload {
|
|
207
268
|
file: string;
|
|
208
269
|
url: string;
|
|
@@ -224,6 +285,8 @@ export interface ManifestScene {
|
|
|
224
285
|
revoiced: boolean | null;
|
|
225
286
|
speechTrimmed: boolean | null;
|
|
226
287
|
rawStorageId: string | null;
|
|
288
|
+
voiceMode: "voice-first" | null;
|
|
289
|
+
voiceFirstBlockedBy: string[] | null;
|
|
227
290
|
clipStatus: string;
|
|
228
291
|
error: string | null;
|
|
229
292
|
flagged: boolean;
|
|
@@ -245,6 +308,14 @@ export interface ManifestCastRef {
|
|
|
245
308
|
voiceLabel: string | null;
|
|
246
309
|
voiceDescription: string | null;
|
|
247
310
|
}
|
|
311
|
+
export type MusicBedState = "on" | "off" | "unknown";
|
|
312
|
+
export interface RunVideoChoice {
|
|
313
|
+
videoModel: string;
|
|
314
|
+
videoModelLabel: string;
|
|
315
|
+
voiceMode: string;
|
|
316
|
+
voiceModeLabel: string;
|
|
317
|
+
}
|
|
318
|
+
export declare const MUSIC_HEARD_CODE = "music-heard";
|
|
248
319
|
export interface VideoManifest {
|
|
249
320
|
runId: string;
|
|
250
321
|
pulledAt: string;
|
|
@@ -252,6 +323,8 @@ export interface VideoManifest {
|
|
|
252
323
|
storyboard: string | null;
|
|
253
324
|
reference: string | null;
|
|
254
325
|
music: string | null;
|
|
326
|
+
musicBed: MusicBedState;
|
|
327
|
+
musicHeardScenes: number[];
|
|
255
328
|
cast: ManifestCastRef[];
|
|
256
329
|
narration: {
|
|
257
330
|
file: string;
|
|
@@ -260,8 +333,13 @@ export interface VideoManifest {
|
|
|
260
333
|
scenes: ManifestScene[];
|
|
261
334
|
failed: PullFailure[];
|
|
262
335
|
provenance?: RunProvenance;
|
|
336
|
+
videoChoice?: {
|
|
337
|
+
videoModel: string;
|
|
338
|
+
voiceMode: string;
|
|
339
|
+
};
|
|
263
340
|
}
|
|
264
|
-
export declare const CAST_LEDGER_BASE
|
|
341
|
+
export declare const CAST_LEDGER_BASE: number;
|
|
342
|
+
export declare function musicBedState(run: VideoRun): MusicBedState;
|
|
265
343
|
export interface PullPlan {
|
|
266
344
|
downloads: PullDownload[];
|
|
267
345
|
texts: PullTextFile[];
|
|
@@ -284,7 +362,39 @@ export interface StartOptions {
|
|
|
284
362
|
wait: boolean;
|
|
285
363
|
json: boolean;
|
|
286
364
|
}
|
|
365
|
+
export interface ScriptStartOptions {
|
|
366
|
+
scriptFile: string;
|
|
367
|
+
conceit: (typeof CONCEIT_KEYS)[number];
|
|
368
|
+
style: string;
|
|
369
|
+
direction?: string;
|
|
370
|
+
voicePath?: string;
|
|
371
|
+
videoModel?: string;
|
|
372
|
+
voiceMode?: VoiceMode;
|
|
373
|
+
music?: boolean;
|
|
374
|
+
wait: boolean;
|
|
375
|
+
json: boolean;
|
|
376
|
+
}
|
|
377
|
+
export type VideoStartPlan = {
|
|
378
|
+
kind: "usage";
|
|
379
|
+
line: string;
|
|
380
|
+
} | {
|
|
381
|
+
kind: "show";
|
|
382
|
+
opts: StartOptions;
|
|
383
|
+
} | {
|
|
384
|
+
kind: "script";
|
|
385
|
+
opts: ScriptStartOptions;
|
|
386
|
+
};
|
|
287
387
|
export declare function startFlow(opts: StartOptions, deps: VideoDeps): Promise<FlowResult>;
|
|
388
|
+
export type MusicChoice = {
|
|
389
|
+
ok: true;
|
|
390
|
+
music: boolean | undefined;
|
|
391
|
+
} | {
|
|
392
|
+
ok: false;
|
|
393
|
+
line: string;
|
|
394
|
+
};
|
|
395
|
+
export declare function planMusicChoice(occurrences: FlagOccurrence[]): MusicChoice;
|
|
396
|
+
export declare function planVideoStart(flags: Record<string, string | boolean>, occurrences: FlagOccurrence[]): VideoStartPlan;
|
|
397
|
+
export declare function startScriptFlow(opts: ScriptStartOptions, deps: VideoDeps): Promise<FlowResult>;
|
|
288
398
|
export declare function waitFlow(runId: string, opts: {
|
|
289
399
|
json: boolean;
|
|
290
400
|
url?: string;
|
|
@@ -292,8 +402,12 @@ export declare function waitFlow(runId: string, opts: {
|
|
|
292
402
|
maxPolls?: number;
|
|
293
403
|
}, deps: VideoDeps): Promise<FlowResult>;
|
|
294
404
|
export declare function statusFlow(runId: string, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
405
|
+
export declare function rejectedDraftFlow(runId: string, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
295
406
|
export declare function storyboardFlow(runId: string, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
296
|
-
export declare function approveFlow(runId: string,
|
|
407
|
+
export declare function approveFlow(runId: string, opts: {
|
|
408
|
+
json: boolean;
|
|
409
|
+
approveStaleCut: boolean;
|
|
410
|
+
}, deps: VideoDeps): Promise<FlowResult>;
|
|
297
411
|
export declare function flagFlow(runId: string, note: string, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
298
412
|
export declare function retryFrameFlow(runId: string, nodeId: string, sceneIndex: number, note: string | undefined, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
299
413
|
export type ClipRedoPlan = {
|
|
@@ -311,7 +425,11 @@ export interface ClipRedoTarget {
|
|
|
311
425
|
nodeId?: string;
|
|
312
426
|
}
|
|
313
427
|
export declare function planClipRedo(run: VideoRun, items: NodeItem[], target: ClipRedoTarget): ClipRedoPlan;
|
|
314
|
-
export
|
|
428
|
+
export interface ClipRedoOptions {
|
|
429
|
+
note?: string;
|
|
430
|
+
voiceFirst?: boolean;
|
|
431
|
+
}
|
|
432
|
+
export declare function retryClipFlow(runId: string, target: ClipRedoTarget, { note, voiceFirst }: ClipRedoOptions, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
315
433
|
export declare const VOICE_PATHS_NEVER_HEARD: Set<string>;
|
|
316
434
|
export declare function planClipRevoice(run: VideoRun, items: NodeItem[], target: ClipRedoTarget): ClipRedoPlan;
|
|
317
435
|
export type RevoiceTarget = ClipRedoTarget | {
|
|
@@ -379,3 +497,4 @@ export declare function describeFetchFailure(err: unknown): string;
|
|
|
379
497
|
export declare function uploadFlow(runId: string, filePath: string, durationFlag: string | undefined, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
380
498
|
export declare function parsePositional(args?: string[]): string[];
|
|
381
499
|
export declare function run(flags: Record<string, string | boolean>, occurrences: FlagOccurrence[]): Promise<void>;
|
|
500
|
+
export {};
|