@aicopycoders/exodus 2026.10.1200-beta.2 → 2026.10.1200

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.
@@ -79,9 +79,11 @@ export type ArtifactSubset = {
79
79
  type: "image";
80
80
  imageUrl?: string;
81
81
  storageId?: string;
82
- anchor?: "set-plate" | "product-sheet" | "filled-plate";
82
+ anchor?: "set-plate" | "product-sheet" | "filled-plate" | "product-plate" | "layout-plate";
83
83
  holds?: string[];
84
84
  lid?: "closed";
85
+ camera?: string;
86
+ view?: "side" | "back" | "three-quarter";
85
87
  } | {
86
88
  type: "video";
87
89
  sceneIndex?: number;
@@ -158,6 +160,7 @@ export interface PullCastLockMember {
158
160
  identityRefs?: Array<{
159
161
  storageId?: string;
160
162
  imageUrl?: string;
163
+ view?: string;
161
164
  }>;
162
165
  }
163
166
  export interface PullCastLock {
@@ -363,6 +366,11 @@ export interface ManifestCastRef {
363
366
  voiceId: string | null;
364
367
  voiceLabel: string | null;
365
368
  voiceDescription: string | null;
369
+ views?: ManifestCastView[];
370
+ }
371
+ export interface ManifestCastView {
372
+ view: string;
373
+ file: string | null;
366
374
  }
367
375
  export type MusicBedState = "on" | "off" | "unknown";
368
376
  export interface RunVideoChoice {
@@ -409,6 +417,7 @@ export interface RunShots {
409
417
  pacing?: string;
410
418
  }
411
419
  export declare const CAST_LEDGER_BASE: number;
420
+ export declare const CAST_VIEW_LEDGER_BASE: number;
412
421
  export declare function musicBedState(run: VideoRun): MusicBedState;
413
422
  export interface PullPlan {
414
423
  downloads: PullDownload[];
@@ -639,6 +639,7 @@ export function planFailureLines(node, runId) {
639
639
  }
640
640
  export const MUSIC_HEARD_CODE = "music-heard";
641
641
  export const CAST_LEDGER_BASE = HELPER_LEDGER_BASE + 10000;
642
+ export const CAST_VIEW_LEDGER_BASE = CAST_LEDGER_BASE + 1000;
642
643
  export function musicBedState(run) {
643
644
  if (run.musicBed === undefined)
644
645
  return "unknown";
@@ -737,9 +738,25 @@ function castVoicePins(artifact) {
737
738
  }
738
739
  return pins;
739
740
  }
741
+ function lockViews(member, viewRows) {
742
+ return (member.identityRefs ?? []).flatMap((ref) => {
743
+ if (!ref.view)
744
+ return [];
745
+ const row = viewRows.find(({ artifact: art }) => art?.type !== "image"
746
+ ? false
747
+ : art.storageId
748
+ ? art.storageId === ref.storageId
749
+ : !!art.imageUrl && art.imageUrl === ref.imageUrl);
750
+ const url = (row?.artifact?.type === "image" ? row.artifact.imageUrl : undefined) ?? ref.imageUrl;
751
+ return url ? [{ view: ref.view, url }] : [];
752
+ });
753
+ }
740
754
  function planCastRefs(run, items, pins) {
741
755
  const ledger = reservedCastFrames(items);
742
- const ledgerRows = [...ledger.entries()].sort(([a], [b]) => a - b);
756
+ const isViewRow = ([sceneIndex, row]) => sceneIndex >= CAST_VIEW_LEDGER_BASE || (row.artifact?.type === "image" && row.artifact.view !== undefined);
757
+ const sorted = [...ledger.entries()].sort(([a], [b]) => a - b);
758
+ const ledgerRows = sorted.filter((r) => !isViewRow(r));
759
+ const viewRows = sorted.filter(isViewRow).map(([, row]) => row);
743
760
  const locked = run.castLock?.cast;
744
761
  const rows = [];
745
762
  if (locked !== undefined && locked.length > 0) {
@@ -765,6 +782,7 @@ function planCastRefs(run, items, pins) {
765
782
  status: item?.status ?? (url ? "done" : "missing"),
766
783
  error: item?.error ?? null,
767
784
  stem: `cast-${member.characterId}`,
785
+ views: lockViews(member, viewRows),
768
786
  });
769
787
  }
770
788
  for (const [sceneIndex, item] of ledgerRows) {
@@ -780,6 +798,7 @@ function planCastRefs(run, items, pins) {
780
798
  status: item.status,
781
799
  error: item.error ?? null,
782
800
  stem: `cast-ref-${String(sceneIndex - CAST_LEDGER_BASE).padStart(2, "0")}`,
801
+ views: [],
783
802
  });
784
803
  }
785
804
  }
@@ -794,6 +813,7 @@ function planCastRefs(run, items, pins) {
794
813
  status: item.status,
795
814
  error: item.error ?? null,
796
815
  stem: `cast-ref-${String(sceneIndex - CAST_LEDGER_BASE).padStart(2, "0")}`,
816
+ views: [],
797
817
  });
798
818
  }
799
819
  }
@@ -802,6 +822,7 @@ function planCastRefs(run, items, pins) {
802
822
  for (const row of rows) {
803
823
  const file = row.url ? `${row.stem}.${extFor(row.url, "image")}` : null;
804
824
  const pin = row.characterId ? pins.get(row.characterId) : undefined;
825
+ const views = row.views.map((v) => ({ view: v.view, file: `${row.stem}-${v.view}.${extFor(v.url, "image")}`, url: v.url }));
805
826
  cast.push({
806
827
  characterId: row.characterId,
807
828
  name: row.name,
@@ -811,9 +832,12 @@ function planCastRefs(run, items, pins) {
811
832
  voiceId: pin?.voiceId ?? null,
812
833
  voiceLabel: pin?.voiceLabel ?? null,
813
834
  voiceDescription: pin?.voiceDescription ?? null,
835
+ ...(views.length ? { views: views.map(({ view, file: viewFile }) => ({ view, file: viewFile })) } : {}),
814
836
  });
815
837
  if (file && row.url)
816
838
  downloads.push({ file, url: row.url });
839
+ for (const v of views)
840
+ downloads.push({ file: v.file, url: v.url });
817
841
  }
818
842
  return { cast, downloads };
819
843
  }
@@ -1105,6 +1129,10 @@ export function markPullFailure(manifest, failure) {
1105
1129
  for (const ref of manifest.cast) {
1106
1130
  if (ref.file === failure.file)
1107
1131
  ref.file = null;
1132
+ for (const view of ref.views ?? []) {
1133
+ if (view.file === failure.file)
1134
+ view.file = null;
1135
+ }
1108
1136
  }
1109
1137
  if (manifest.narration?.file === failure.file)
1110
1138
  manifest.narration = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicopycoders/exodus",
3
- "version": "2026.10.1200-beta.2",
3
+ "version": "2026.10.1200",
4
4
  "description": "CLI + Claude Code skills for the Copycoders Exodus ad pipeline",
5
5
  "repository": {
6
6
  "type": "git",