@aicopycoders/exodus 2026.9.2000-beta.2 → 2026.9.2000-beta.21
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 +201 -39
- 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 +137 -7
- package/dist/commands/video.js +778 -64
- package/dist/commands/workflow.d.ts +53 -4
- package/dist/commands/workflow.js +76 -10
- 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,30 @@ 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
|
+
autoRedoScenes?: number[];
|
|
122
|
+
}
|
|
123
|
+
export interface MissingScene {
|
|
124
|
+
sceneIndex: number;
|
|
125
|
+
pictureRefused: boolean;
|
|
79
126
|
}
|
|
80
127
|
export type BuilderPauseReason = "taste" | "repair" | "slots" | "call" | "checkpoint";
|
|
81
128
|
export interface PullCastLockMember {
|
|
@@ -103,6 +150,13 @@ export interface VideoRun {
|
|
|
103
150
|
pauseAhead?: WorkflowRun["pauseAhead"];
|
|
104
151
|
castLock?: PullCastLock | null;
|
|
105
152
|
provenance?: RunProvenance;
|
|
153
|
+
musicBed?: boolean;
|
|
154
|
+
videoChoice?: RunVideoChoice;
|
|
155
|
+
storyboardAutoApproval?: {
|
|
156
|
+
outcome: "approved" | "held";
|
|
157
|
+
at: number;
|
|
158
|
+
reason?: string;
|
|
159
|
+
};
|
|
106
160
|
workflowId?: string;
|
|
107
161
|
moduleOwned?: boolean;
|
|
108
162
|
}
|
|
@@ -121,6 +175,11 @@ export interface NodeItem {
|
|
|
121
175
|
take: number;
|
|
122
176
|
outcome: string;
|
|
123
177
|
label: string;
|
|
178
|
+
reason?: string;
|
|
179
|
+
displacedHistory?: {
|
|
180
|
+
title: string;
|
|
181
|
+
lines: string[];
|
|
182
|
+
};
|
|
124
183
|
};
|
|
125
184
|
artifact?: ArtifactSubset;
|
|
126
185
|
}
|
|
@@ -169,12 +228,17 @@ export declare function videoApiError(res: ApiResponse<unknown>): string;
|
|
|
169
228
|
export type RunStop = {
|
|
170
229
|
at: "running";
|
|
171
230
|
stage: string;
|
|
231
|
+
autoRedoScenes?: number[];
|
|
172
232
|
} | {
|
|
173
233
|
at: "storyboard-gate";
|
|
174
234
|
nodeId?: string;
|
|
175
235
|
framesNodeId?: string;
|
|
236
|
+
showAd?: true;
|
|
237
|
+
heldReason?: string;
|
|
176
238
|
} | {
|
|
177
239
|
at: "final-watch";
|
|
240
|
+
missingScenes?: MissingScene[];
|
|
241
|
+
framesNodeId?: string;
|
|
178
242
|
} | {
|
|
179
243
|
at: "paused";
|
|
180
244
|
nodeId?: string;
|
|
@@ -191,18 +255,24 @@ export type RunStop = {
|
|
|
191
255
|
};
|
|
192
256
|
export type ResolvedStop = Exclude<RunStop, {
|
|
193
257
|
at: "final-watch";
|
|
194
|
-
}> | {
|
|
258
|
+
}> | (Extract<RunStop, {
|
|
195
259
|
at: "final-watch";
|
|
260
|
+
}> & {
|
|
196
261
|
cutAttached: boolean | null;
|
|
197
|
-
};
|
|
262
|
+
});
|
|
198
263
|
export declare function classifyRun(run: VideoRun): RunStop;
|
|
199
264
|
export declare function hasAttachedCut(items: NodeItem[]): boolean;
|
|
265
|
+
export declare function scenesBeingRedone(run: VideoRun, items: NodeItem[]): number[];
|
|
200
266
|
export declare function resolveStop(stop: RunStop, cutAttached: boolean | null): ResolvedStop;
|
|
201
267
|
export declare function resolveStopAtPark(stop: RunStop, runId: string, deps: Pick<VideoDeps, "get">): Promise<ResolvedStop>;
|
|
202
268
|
export declare function stageWord(stage: string): string;
|
|
203
269
|
export declare function stepName(kind: string | undefined): string;
|
|
204
|
-
export declare function
|
|
270
|
+
export declare function isShowAd(run: Pick<VideoRun, "moduleOwned">): boolean;
|
|
271
|
+
export declare function reviewUrl(dashboardUrl: string, run: Pick<VideoRun, "_id" | "workflowId">): string;
|
|
205
272
|
export declare function stopLines(stop: ResolvedStop, runId: string, runUrl: string): string[];
|
|
273
|
+
export declare function failedStoryboardNode(run: VideoRun): VideoRunNode | undefined;
|
|
274
|
+
export declare function errorEchoesReasons(error: string | undefined, record: PlanFailureRecord | undefined): boolean;
|
|
275
|
+
export declare function planFailureLines(node: VideoRunNode | undefined, runId: string): string[];
|
|
206
276
|
export interface PullDownload {
|
|
207
277
|
file: string;
|
|
208
278
|
url: string;
|
|
@@ -224,6 +294,8 @@ export interface ManifestScene {
|
|
|
224
294
|
revoiced: boolean | null;
|
|
225
295
|
speechTrimmed: boolean | null;
|
|
226
296
|
rawStorageId: string | null;
|
|
297
|
+
voiceMode: "voice-first" | null;
|
|
298
|
+
voiceFirstBlockedBy: string[] | null;
|
|
227
299
|
clipStatus: string;
|
|
228
300
|
error: string | null;
|
|
229
301
|
flagged: boolean;
|
|
@@ -245,6 +317,14 @@ export interface ManifestCastRef {
|
|
|
245
317
|
voiceLabel: string | null;
|
|
246
318
|
voiceDescription: string | null;
|
|
247
319
|
}
|
|
320
|
+
export type MusicBedState = "on" | "off" | "unknown";
|
|
321
|
+
export interface RunVideoChoice {
|
|
322
|
+
videoModel: string;
|
|
323
|
+
videoModelLabel: string;
|
|
324
|
+
voiceMode: string;
|
|
325
|
+
voiceModeLabel: string;
|
|
326
|
+
}
|
|
327
|
+
export declare const MUSIC_HEARD_CODE = "music-heard";
|
|
248
328
|
export interface VideoManifest {
|
|
249
329
|
runId: string;
|
|
250
330
|
pulledAt: string;
|
|
@@ -252,6 +332,9 @@ export interface VideoManifest {
|
|
|
252
332
|
storyboard: string | null;
|
|
253
333
|
reference: string | null;
|
|
254
334
|
music: string | null;
|
|
335
|
+
musicBed: MusicBedState;
|
|
336
|
+
musicHeardScenes: number[];
|
|
337
|
+
pendingRedo: number[];
|
|
255
338
|
cast: ManifestCastRef[];
|
|
256
339
|
narration: {
|
|
257
340
|
file: string;
|
|
@@ -260,8 +343,13 @@ export interface VideoManifest {
|
|
|
260
343
|
scenes: ManifestScene[];
|
|
261
344
|
failed: PullFailure[];
|
|
262
345
|
provenance?: RunProvenance;
|
|
346
|
+
videoChoice?: {
|
|
347
|
+
videoModel: string;
|
|
348
|
+
voiceMode: string;
|
|
349
|
+
};
|
|
263
350
|
}
|
|
264
|
-
export declare const CAST_LEDGER_BASE
|
|
351
|
+
export declare const CAST_LEDGER_BASE: number;
|
|
352
|
+
export declare function musicBedState(run: VideoRun): MusicBedState;
|
|
265
353
|
export interface PullPlan {
|
|
266
354
|
downloads: PullDownload[];
|
|
267
355
|
texts: PullTextFile[];
|
|
@@ -284,7 +372,40 @@ export interface StartOptions {
|
|
|
284
372
|
wait: boolean;
|
|
285
373
|
json: boolean;
|
|
286
374
|
}
|
|
375
|
+
export interface ScriptStartOptions {
|
|
376
|
+
scriptFile: string;
|
|
377
|
+
conceit: (typeof CONCEIT_KEYS)[number];
|
|
378
|
+
style: string;
|
|
379
|
+
direction?: string;
|
|
380
|
+
voicePath?: string;
|
|
381
|
+
videoModel?: string;
|
|
382
|
+
voiceMode?: VoiceMode;
|
|
383
|
+
music?: boolean;
|
|
384
|
+
reviewStoryboard?: true;
|
|
385
|
+
wait: boolean;
|
|
386
|
+
json: boolean;
|
|
387
|
+
}
|
|
388
|
+
export type VideoStartPlan = {
|
|
389
|
+
kind: "usage";
|
|
390
|
+
line: string;
|
|
391
|
+
} | {
|
|
392
|
+
kind: "show";
|
|
393
|
+
opts: StartOptions;
|
|
394
|
+
} | {
|
|
395
|
+
kind: "script";
|
|
396
|
+
opts: ScriptStartOptions;
|
|
397
|
+
};
|
|
287
398
|
export declare function startFlow(opts: StartOptions, deps: VideoDeps): Promise<FlowResult>;
|
|
399
|
+
export type MusicChoice = {
|
|
400
|
+
ok: true;
|
|
401
|
+
music: boolean | undefined;
|
|
402
|
+
} | {
|
|
403
|
+
ok: false;
|
|
404
|
+
line: string;
|
|
405
|
+
};
|
|
406
|
+
export declare function planMusicChoice(occurrences: FlagOccurrence[]): MusicChoice;
|
|
407
|
+
export declare function planVideoStart(flags: Record<string, string | boolean>, occurrences: FlagOccurrence[]): VideoStartPlan;
|
|
408
|
+
export declare function startScriptFlow(opts: ScriptStartOptions, deps: VideoDeps): Promise<FlowResult>;
|
|
288
409
|
export declare function waitFlow(runId: string, opts: {
|
|
289
410
|
json: boolean;
|
|
290
411
|
url?: string;
|
|
@@ -292,8 +413,12 @@ export declare function waitFlow(runId: string, opts: {
|
|
|
292
413
|
maxPolls?: number;
|
|
293
414
|
}, deps: VideoDeps): Promise<FlowResult>;
|
|
294
415
|
export declare function statusFlow(runId: string, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
416
|
+
export declare function rejectedDraftFlow(runId: string, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
295
417
|
export declare function storyboardFlow(runId: string, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
296
|
-
export declare function approveFlow(runId: string,
|
|
418
|
+
export declare function approveFlow(runId: string, opts: {
|
|
419
|
+
json: boolean;
|
|
420
|
+
approveStaleCut: boolean;
|
|
421
|
+
}, deps: VideoDeps): Promise<FlowResult>;
|
|
297
422
|
export declare function flagFlow(runId: string, note: string, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
298
423
|
export declare function retryFrameFlow(runId: string, nodeId: string, sceneIndex: number, note: string | undefined, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
299
424
|
export type ClipRedoPlan = {
|
|
@@ -311,7 +436,11 @@ export interface ClipRedoTarget {
|
|
|
311
436
|
nodeId?: string;
|
|
312
437
|
}
|
|
313
438
|
export declare function planClipRedo(run: VideoRun, items: NodeItem[], target: ClipRedoTarget): ClipRedoPlan;
|
|
314
|
-
export
|
|
439
|
+
export interface ClipRedoOptions {
|
|
440
|
+
note?: string;
|
|
441
|
+
voiceFirst?: boolean;
|
|
442
|
+
}
|
|
443
|
+
export declare function retryClipFlow(runId: string, target: ClipRedoTarget, { note, voiceFirst }: ClipRedoOptions, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
315
444
|
export declare const VOICE_PATHS_NEVER_HEARD: Set<string>;
|
|
316
445
|
export declare function planClipRevoice(run: VideoRun, items: NodeItem[], target: ClipRedoTarget): ClipRedoPlan;
|
|
317
446
|
export type RevoiceTarget = ClipRedoTarget | {
|
|
@@ -379,3 +508,4 @@ export declare function describeFetchFailure(err: unknown): string;
|
|
|
379
508
|
export declare function uploadFlow(runId: string, filePath: string, durationFlag: string | undefined, json: boolean, deps: VideoDeps): Promise<FlowResult>;
|
|
380
509
|
export declare function parsePositional(args?: string[]): string[];
|
|
381
510
|
export declare function run(flags: Record<string, string | boolean>, occurrences: FlagOccurrence[]): Promise<void>;
|
|
511
|
+
export {};
|