@aicopycoders/exodus 2026.9.2000-beta.23 → 2026.9.2000-beta.24

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.
@@ -63,7 +63,8 @@ export type ArtifactSubset = {
63
63
  type: "image";
64
64
  imageUrl?: string;
65
65
  storageId?: string;
66
- anchor?: "set-plate" | "product-sheet";
66
+ anchor?: "set-plate" | "product-sheet" | "filled-plate";
67
+ holds?: string[];
67
68
  } | {
68
69
  type: "video";
69
70
  sceneIndex?: number;
@@ -342,6 +343,10 @@ export interface RunVideoChoice {
342
343
  voiceModeLabel: string;
343
344
  }
344
345
  export declare const MUSIC_HEARD_CODE = "music-heard";
346
+ export interface ManifestFilledPlate {
347
+ file: string | null;
348
+ holds: string[];
349
+ }
345
350
  export interface VideoManifest {
346
351
  runId: string;
347
352
  pulledAt: string;
@@ -350,6 +355,7 @@ export interface VideoManifest {
350
355
  reference: string | null;
351
356
  productSheet: string | null;
352
357
  setPlate: string | null;
358
+ filledPlates: ManifestFilledPlate[];
353
359
  music: string | null;
354
360
  musicBed: MusicBedState;
355
361
  musicHeardScenes: number[];
@@ -140,8 +140,9 @@ Options:
140
140
  --voice-path <path> How the voices are made (start)
141
141
  --video-model <id> Which video model films this ad, instead of the kind of
142
142
  ad's own default (start, with --conceit)
143
- --voice <mode> native: the video model speaks the lines. voice-first:
144
- the lines are recorded first (start, with --conceit)
143
+ --voice <mode> voice-first (the default): the lines are recorded
144
+ first. native: the video model speaks the lines
145
+ (start, with --conceit)
145
146
  --music Put a music bed under the ad (start). An ad has no music
146
147
  unless you ask for it here
147
148
  --no-music Leave the music bed off (start), which it already is
@@ -790,9 +791,15 @@ export function planPull(run, items, opts) {
790
791
  let reference = null;
791
792
  let productSheet = null;
792
793
  let setPlate = null;
794
+ const filledByStage = new Map();
793
795
  for (const artifact of outputsOfNodeKind(run, "reference")) {
794
796
  if (artifact.type !== "image" || !artifact.imageUrl)
795
797
  continue;
798
+ if (artifact.anchor === "filled-plate") {
799
+ const holds = artifact.holds ?? [];
800
+ filledByStage.set(JSON.stringify(holds), { holds, url: artifact.imageUrl });
801
+ continue;
802
+ }
796
803
  const stem = artifact.anchor ?? "reference";
797
804
  const download = { file: `${stem}.${extFor(artifact.imageUrl, "image")}`, url: artifact.imageUrl };
798
805
  if (artifact.anchor === "product-sheet")
@@ -802,6 +809,10 @@ export function planPull(run, items, opts) {
802
809
  else if (!artifact.anchor)
803
810
  reference = download;
804
811
  }
812
+ const filledPlates = [...filledByStage.values()].map((plate, i) => ({
813
+ holds: plate.holds,
814
+ download: { file: `filled-plate-${i + 1}.${extFor(plate.url, "image")}`, url: plate.url },
815
+ }));
805
816
  const keyframeByScene = new Map();
806
817
  for (const artifact of run.nodes.flatMap((n) => n.outputs ?? [])) {
807
818
  if (artifact.type !== "frames")
@@ -969,6 +980,7 @@ export function planPull(run, items, opts) {
969
980
  ...(reference ? [reference] : []),
970
981
  ...(productSheet ? [productSheet] : []),
971
982
  ...(setPlate ? [setPlate] : []),
983
+ ...filledPlates.map((p) => p.download),
972
984
  ...keyframeByScene.values(),
973
985
  ...[...voiceByScene.values()].map((v) => v.download),
974
986
  ...(narrationDownload ? [narrationDownload] : []),
@@ -987,6 +999,7 @@ export function planPull(run, items, opts) {
987
999
  reference: reference?.file ?? null,
988
1000
  productSheet: productSheet?.file ?? null,
989
1001
  setPlate: setPlate?.file ?? null,
1002
+ filledPlates: filledPlates.map((p) => ({ file: p.download.file, holds: p.holds })),
990
1003
  music: music?.file ?? null,
991
1004
  musicBed: musicBedState(run),
992
1005
  musicHeardScenes: scenes
@@ -1023,6 +1036,10 @@ export function markPullFailure(manifest, failure) {
1023
1036
  manifest.productSheet = null;
1024
1037
  if (manifest.setPlate === failure.file)
1025
1038
  manifest.setPlate = null;
1039
+ for (const plate of manifest.filledPlates) {
1040
+ if (plate.file === failure.file)
1041
+ plate.file = null;
1042
+ }
1026
1043
  if (manifest.music === failure.file)
1027
1044
  manifest.music = null;
1028
1045
  for (const ref of manifest.cast) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicopycoders/exodus",
3
- "version": "2026.9.2000-beta.23",
3
+ "version": "2026.9.2000-beta.24",
4
4
  "description": "CLI + Claude Code skills for the Copycoders Exodus ad pipeline",
5
5
  "repository": {
6
6
  "type": "git",