@aicopycoders/exodus 2026.10.1701-beta.1 → 2026.10.1900-beta.1
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/skills/exodus-video/SKILL.md +12 -2
- package/dist/commands/video-customer.d.ts +1 -0
- package/dist/commands/video.d.ts +1 -0
- package/dist/commands/video.js +74 -10
- package/dist/lib/runStatus.d.ts +1 -1
- package/dist/lib/runStatus.js +2 -2
- package/dist/lib/runVerdict.d.ts +1 -0
- package/dist/lib/runVerdict.js +2 -2
- package/package.json +1 -1
|
@@ -101,7 +101,7 @@ never how it is made. Every answer to them says `"view": "customer"`, and the
|
|
|
101
101
|
CLI words it the way the run page does. On their own brands they can:
|
|
102
102
|
|
|
103
103
|
- `video start --script <file> --conceit <key> --style <slug>` with
|
|
104
|
-
`--direction`, `--frame`, `--pacing`, `--music`, `--one-shot`, `--review-storyboard`, `--stop-at-script` and `--wait`. Any other start
|
|
104
|
+
`--direction`, `--frame`, `--pacing`, `--music`, `--one-shot`, `--review-storyboard`, `--stop-at-script`, `--rewrite-to-fit` and `--wait`. Any other start
|
|
105
105
|
flag is refused with one sentence naming it. The answer is the run id and the
|
|
106
106
|
link to the video's page, plus what the server did to the script: laid a
|
|
107
107
|
plain transcript out, or wrote a script from a description (see "The script
|
|
@@ -158,7 +158,7 @@ exodus workflow run <workflowId|name> --input <field>=@script.txt --voices @voic
|
|
|
158
158
|
exodus workflow run <workflowId|name> --input <field>=@script.txt --voice-treatment @voice-treatment.json --wait
|
|
159
159
|
same, with the video model speaking voices you WROTE
|
|
160
160
|
exodus video status <runId> where it is, per scene
|
|
161
|
-
exodus video start --script <file> --conceit <kind> --style <look> [--direction "…"] [--frame pov] [--pacing <jump-cuts|long-take|montage>] [--voice-path <path>] [--video-model <id>] [--voice native] [--music] [--one-shot] [--director] [--stop-at-script] [--wait]
|
|
161
|
+
exodus video start --script <file> --conceit <kind> --style <look> [--direction "…"] [--frame pov] [--pacing <jump-cuts|long-take|montage>] [--voice-path <path>] [--video-model <id>] [--voice native] [--music] [--one-shot] [--director] [--stop-at-script] [--rewrite-to-fit] [--wait]
|
|
162
162
|
start from a script and the kinds the server offers.
|
|
163
163
|
The file can be a plain transcript, a draft or a
|
|
164
164
|
description: the server tidies it (see "The script
|
|
@@ -397,6 +397,16 @@ What the CLI prints, and what you tell the user:
|
|
|
397
397
|
one-person kind of ad): the notes come first, then the refusal, and `--json`
|
|
398
398
|
carries the tidied script under `script`. Fix that script and send it. It is
|
|
399
399
|
already in the format, so it is not tidied or paid for again.
|
|
400
|
+
- **The script doesn't fit the kind of ad** (too many speakers, or a rule the
|
|
401
|
+
kind of ad has): the refusal says `--rewrite-to-fit` can fix it, and
|
|
402
|
+
`--json` carries `rewriteOffered: true`. Ask the user before you add the
|
|
403
|
+
flag: the server then writes a new script for that kind of ad from theirs
|
|
404
|
+
(the dashboard's "Rewrite to fit"), which can change every line and the
|
|
405
|
+
names, starts the run on it, and prints it (`rewritten` in `--json`). Show
|
|
406
|
+
the user that script. To let them read it first, add `--stop-at-script` too.
|
|
407
|
+
If the rewrite can't be made, nothing starts and the error says why
|
|
408
|
+
(`SCRIPT_NOT_REWRITTEN`). The other way out is a kind of ad made for the
|
|
409
|
+
script's cast.
|
|
400
410
|
|
|
401
411
|
**`--stop-at-script`** prints the script the run would start on and starts
|
|
402
412
|
nothing (exit 0). A question or a description too thin to write from answers
|
package/dist/commands/video.d.ts
CHANGED
package/dist/commands/video.js
CHANGED
|
@@ -168,6 +168,13 @@ Options:
|
|
|
168
168
|
--stop-at-script Print the script the run would start on, and start
|
|
169
169
|
nothing (start, with --conceit). To read a written or
|
|
170
170
|
laid-out script first; send it back with --script
|
|
171
|
+
--rewrite-to-fit If the script doesn't fit the kind of ad (more speakers
|
|
172
|
+
than it takes, or a rule it has), write a new script for
|
|
173
|
+
it from yours, print it, and start on it (start, with
|
|
174
|
+
--conceit). That can change every line and the names.
|
|
175
|
+
A script that fits starts as it is. With
|
|
176
|
+
--stop-at-script, it prints the new script and starts
|
|
177
|
+
nothing
|
|
171
178
|
--conceit <kind> Kind of ad (start)
|
|
172
179
|
--style <look> Look (start)
|
|
173
180
|
The server lists the kinds and looks it offers. An unknown word prints them.
|
|
@@ -207,7 +214,7 @@ Options:
|
|
|
207
214
|
Who can use what. Admins get every command here. Beta testers with video
|
|
208
215
|
switched on get these, on their own brands: start (--script, --conceit,
|
|
209
216
|
--style, --direction, --frame, --pacing, --music, --one-shot,
|
|
210
|
-
--review-storyboard, --stop-at-script),
|
|
217
|
+
--review-storyboard, --stop-at-script, --rewrite-to-fit),
|
|
211
218
|
status, storyboard, approve (the storyboard), voices, retry-frame --scene <n> (redo one scene's
|
|
212
219
|
picture while the storyboard waits), pull (the finished video, each scene's
|
|
213
220
|
clip and voice, and the music) and upload. These stay admin-only:
|
|
@@ -1457,6 +1464,20 @@ export function planVideoStart(flags, occurrences) {
|
|
|
1457
1464
|
line: "--stop-at-script works with --conceit, not --show. A Show's script is read as you wrote it.",
|
|
1458
1465
|
};
|
|
1459
1466
|
}
|
|
1467
|
+
const refitFlags = occurrences.filter((o) => o.flag === "rewrite-to-fit");
|
|
1468
|
+
if (refitFlags.some((o) => o.value !== undefined && !o.value.startsWith("--"))) {
|
|
1469
|
+
return {
|
|
1470
|
+
kind: "usage",
|
|
1471
|
+
line: "--rewrite-to-fit takes no value. Leave it off to start the script as you wrote it.",
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
const rewriteToFit = refitFlags.length > 0;
|
|
1475
|
+
if (showId && rewriteToFit) {
|
|
1476
|
+
return {
|
|
1477
|
+
kind: "usage",
|
|
1478
|
+
line: "--rewrite-to-fit works with --conceit, not --show. A Show's script is read as you wrote it.",
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1460
1481
|
if (showId && director) {
|
|
1461
1482
|
return {
|
|
1462
1483
|
kind: "usage",
|
|
@@ -1531,6 +1552,8 @@ export function planVideoStart(flags, occurrences) {
|
|
|
1531
1552
|
opts.director = true;
|
|
1532
1553
|
if (stopAtScript)
|
|
1533
1554
|
opts.stopAtScript = true;
|
|
1555
|
+
if (rewriteToFit)
|
|
1556
|
+
opts.rewriteToFit = true;
|
|
1534
1557
|
if (frameFlag.value || pacingFlag.value) {
|
|
1535
1558
|
opts.shots = {
|
|
1536
1559
|
...(frameFlag.value ? { frame: frameFlag.value } : {}),
|
|
@@ -1644,6 +1667,20 @@ function scriptWrittenHead(written, started) {
|
|
|
1644
1667
|
function writtenOf(value) {
|
|
1645
1668
|
return typeof value === "string" && value.trim() !== "" ? value : null;
|
|
1646
1669
|
}
|
|
1670
|
+
function scriptRewrittenHead(rewritten, started) {
|
|
1671
|
+
if (rewritten === null)
|
|
1672
|
+
return [];
|
|
1673
|
+
return [
|
|
1674
|
+
started
|
|
1675
|
+
? "Your script didn't fit this kind of ad, so we wrote a new one from it and started the run on it:"
|
|
1676
|
+
: "Your script didn't fit this kind of ad, so we wrote a new one from it:",
|
|
1677
|
+
"",
|
|
1678
|
+
...rewritten.split("\n").map((line) => (line ? ` ${line}` : "")),
|
|
1679
|
+
"",
|
|
1680
|
+
];
|
|
1681
|
+
}
|
|
1682
|
+
const REWRITE_OFFER = "To have a new script written from yours for this kind of ad, run the same command with --rewrite-to-fit. " +
|
|
1683
|
+
"That can change every line and the names.";
|
|
1647
1684
|
function scriptNeedsCallerResult(res, json) {
|
|
1648
1685
|
const data = res.data;
|
|
1649
1686
|
if (!isRecord(data) || !isRecord(data.error))
|
|
@@ -1667,7 +1704,9 @@ function scriptStartRefused(res, json) {
|
|
|
1667
1704
|
const data = isRecord(res.data) ? res.data : {};
|
|
1668
1705
|
const tidied = data.tidied === undefined ? null : scriptTidiedOf(data.tidied);
|
|
1669
1706
|
const written = writtenOf(data.written);
|
|
1670
|
-
|
|
1707
|
+
const rewritten = writtenOf(data.rewritten);
|
|
1708
|
+
const offered = data.rewriteOffered === true;
|
|
1709
|
+
if (!tidied && written === null && rewritten === null && !offered)
|
|
1671
1710
|
return errorResult(res, json);
|
|
1672
1711
|
const script = typeof data.script === "string" ? data.script : null;
|
|
1673
1712
|
const error = videoApiError(res);
|
|
@@ -1681,16 +1720,25 @@ function scriptStartRefused(res, json) {
|
|
|
1681
1720
|
error,
|
|
1682
1721
|
...(tidied ? { tidied } : {}),
|
|
1683
1722
|
...(written !== null ? { written } : {}),
|
|
1723
|
+
...(rewritten !== null ? { rewritten } : {}),
|
|
1684
1724
|
...(script !== null ? { script } : {}),
|
|
1725
|
+
...(offered ? { rewriteOffered: true } : {}),
|
|
1685
1726
|
}),
|
|
1686
1727
|
]
|
|
1687
|
-
: [
|
|
1728
|
+
: [
|
|
1729
|
+
...scriptTidiedHead(tidied),
|
|
1730
|
+
...scriptWrittenHead(written, false),
|
|
1731
|
+
...scriptRewrittenHead(rewritten, false),
|
|
1732
|
+
error,
|
|
1733
|
+
...(offered ? [REWRITE_OFFER] : []),
|
|
1734
|
+
],
|
|
1688
1735
|
};
|
|
1689
1736
|
}
|
|
1690
1737
|
function stoppedAtScriptResult(data, json) {
|
|
1691
1738
|
const script = typeof data.script === "string" ? data.script : "";
|
|
1692
1739
|
const tidied = data.tidied === undefined ? null : scriptTidiedOf(data.tidied);
|
|
1693
1740
|
const written = writtenOf(data.written);
|
|
1741
|
+
const rewritten = writtenOf(data.rewritten);
|
|
1694
1742
|
if (json) {
|
|
1695
1743
|
return {
|
|
1696
1744
|
code: 0,
|
|
@@ -1700,6 +1748,7 @@ function stoppedAtScriptResult(data, json) {
|
|
|
1700
1748
|
script,
|
|
1701
1749
|
...(tidied ? { tidied } : {}),
|
|
1702
1750
|
...(written !== null ? { written } : {}),
|
|
1751
|
+
...(rewritten !== null ? { rewritten } : {}),
|
|
1703
1752
|
}),
|
|
1704
1753
|
],
|
|
1705
1754
|
};
|
|
@@ -1708,9 +1757,11 @@ function stoppedAtScriptResult(data, json) {
|
|
|
1708
1757
|
code: 0,
|
|
1709
1758
|
lines: [
|
|
1710
1759
|
...scriptTidiedHead(tidied),
|
|
1711
|
-
|
|
1712
|
-
? "Your
|
|
1713
|
-
:
|
|
1760
|
+
rewritten !== null
|
|
1761
|
+
? "Your script didn't fit this kind of ad, so we wrote a new one from it. Nothing has started."
|
|
1762
|
+
: written !== null
|
|
1763
|
+
? "Your file described the ad, so we wrote this script from it. Nothing has started."
|
|
1764
|
+
: "This is the script the run would start on. Nothing has started.",
|
|
1714
1765
|
"",
|
|
1715
1766
|
...script.split("\n").map((line) => (line ? ` ${line}` : "")),
|
|
1716
1767
|
"",
|
|
@@ -1792,6 +1843,7 @@ export async function startScriptFlow(opts, deps) {
|
|
|
1792
1843
|
...(opts.director ? { director: true } : {}),
|
|
1793
1844
|
...(opts.shots ? { shots: opts.shots } : {}),
|
|
1794
1845
|
...(opts.stopAtScript ? { stopAtScript: true } : {}),
|
|
1846
|
+
...(opts.rewriteToFit ? { rewriteToFit: true } : {}),
|
|
1795
1847
|
});
|
|
1796
1848
|
if (!res.ok)
|
|
1797
1849
|
return scriptNeedsCallerResult(res, opts.json) ?? scriptStartRefused(res, opts.json);
|
|
@@ -1811,13 +1863,19 @@ export async function startScriptFlow(opts, deps) {
|
|
|
1811
1863
|
const video = startedVideoChoice(started.videoChoice);
|
|
1812
1864
|
const tidied = started.tidied === undefined ? null : scriptTidiedOf(started.tidied);
|
|
1813
1865
|
const written = writtenOf(started.written);
|
|
1866
|
+
const rewritten = writtenOf(started.rewritten);
|
|
1814
1867
|
const facts = {
|
|
1815
1868
|
card,
|
|
1816
1869
|
...(video ? { videoChoice: video } : {}),
|
|
1817
1870
|
...(tidied ? { tidied } : {}),
|
|
1818
1871
|
...(written !== null ? { written } : {}),
|
|
1872
|
+
...(rewritten !== null ? { rewritten } : {}),
|
|
1819
1873
|
};
|
|
1820
|
-
const head = [
|
|
1874
|
+
const head = [
|
|
1875
|
+
...scriptTidiedHead(tidied),
|
|
1876
|
+
...scriptWrittenHead(written, true),
|
|
1877
|
+
...scriptRewrittenHead(rewritten, true),
|
|
1878
|
+
];
|
|
1821
1879
|
if (!opts.wait) {
|
|
1822
1880
|
return {
|
|
1823
1881
|
code: 0,
|
|
@@ -1844,16 +1902,22 @@ async function customerStartedResult(started, opts, deps) {
|
|
|
1844
1902
|
const { runId, dashboardUrl } = started;
|
|
1845
1903
|
const tidied = started.tidied === undefined ? null : scriptTidiedOf(started.tidied);
|
|
1846
1904
|
const written = writtenOf(started.written);
|
|
1905
|
+
const rewritten = writtenOf(started.rewritten);
|
|
1847
1906
|
const head = [
|
|
1848
1907
|
...scriptTidiedHead(tidied),
|
|
1849
1908
|
...scriptWrittenHead(written, true),
|
|
1909
|
+
...scriptRewrittenHead(rewritten, true),
|
|
1850
1910
|
`Started video ${runId}`,
|
|
1851
1911
|
`Watch it: ${dashboardUrl}`,
|
|
1852
1912
|
];
|
|
1853
1913
|
if (opts.wait) {
|
|
1854
1914
|
const waited = await startedThenWait(runId, head, { ...opts, url: dashboardUrl }, deps);
|
|
1855
|
-
const facts = {
|
|
1856
|
-
|
|
1915
|
+
const facts = {
|
|
1916
|
+
...(tidied ? { tidied } : {}),
|
|
1917
|
+
...(written !== null ? { written } : {}),
|
|
1918
|
+
...(rewritten !== null ? { rewritten } : {}),
|
|
1919
|
+
};
|
|
1920
|
+
return opts.json && (tidied || written !== null || rewritten !== null) ? waitJsonWith(waited, facts) : waited;
|
|
1857
1921
|
}
|
|
1858
1922
|
if (opts.json)
|
|
1859
1923
|
return { code: 0, lines: [JSON.stringify(started)] };
|
|
@@ -2045,7 +2109,7 @@ export async function statusFlow(runId, json, deps) {
|
|
|
2045
2109
|
if (item.itemKind !== "clip" && item.itemKind !== "voiceover" && item.itemKind !== "frame") {
|
|
2046
2110
|
continue;
|
|
2047
2111
|
}
|
|
2048
|
-
if (item.sceneIndex
|
|
2112
|
+
if (!isPullSceneIndex(item.sceneIndex))
|
|
2049
2113
|
continue;
|
|
2050
2114
|
if (item.itemKind === "frame" && !sceneFrameNodeIds.has(item.nodeId))
|
|
2051
2115
|
continue;
|
package/dist/lib/runStatus.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare function isActiveRunStatus(raw: string | null | undefined): boole
|
|
|
14
14
|
export declare const WORKFLOW_RUN_STATUS_VALUES: readonly ["queued", "running", "awaiting-approval", "succeeded", "succeeded-with-warnings", "failed", "cancelled"];
|
|
15
15
|
export declare const LEGACY_WORKFLOW_RUN_STATUS_VALUES: readonly ["awaiting-review", "completed", "partial", "canceled"];
|
|
16
16
|
export declare function storedWorkflowStatusForms(status: RunStatus): string[];
|
|
17
|
-
export declare function workflowRunPresentation(rawStatus: string | null | undefined, pauseReason?: string | null): {
|
|
17
|
+
export declare function workflowRunPresentation(rawStatus: string | null | undefined, pauseReason?: string | null, workerStopped?: boolean): {
|
|
18
18
|
status: RunStatus;
|
|
19
19
|
label: string;
|
|
20
20
|
tone: RunStatusTone;
|
package/dist/lib/runStatus.js
CHANGED
|
@@ -115,7 +115,7 @@ export function storedWorkflowStatusForms(status) {
|
|
|
115
115
|
const twin = LEGACY_WORKFLOW_TWIN[status];
|
|
116
116
|
return twin ? [status, twin] : [status];
|
|
117
117
|
}
|
|
118
|
-
export function workflowRunPresentation(rawStatus, pauseReason) {
|
|
118
|
+
export function workflowRunPresentation(rawStatus, pauseReason, workerStopped) {
|
|
119
119
|
let status = normalizeRunStatus(rawStatus);
|
|
120
120
|
let detail;
|
|
121
121
|
if (status === "awaiting-approval") {
|
|
@@ -127,7 +127,7 @@ export function workflowRunPresentation(rawStatus, pauseReason) {
|
|
|
127
127
|
detail = "needs inputs";
|
|
128
128
|
}
|
|
129
129
|
else if (pauseReason === "repair") {
|
|
130
|
-
detail = "needs repair";
|
|
130
|
+
detail = workerStopped ? "worker stopped" : "needs repair";
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
return {
|
package/dist/lib/runVerdict.d.ts
CHANGED
package/dist/lib/runVerdict.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RUN_STATUS_LABELS, workflowRunPresentation } from "./runStatus.js";
|
|
1
|
+
import { RUN_STATUS_LABELS, isWorkerStoppedPark, workflowRunPresentation, } from "./runStatus.js";
|
|
2
2
|
export function summarizeDeliveries(deliveries) {
|
|
3
3
|
if (deliveries === undefined || deliveries === null)
|
|
4
4
|
return undefined;
|
|
@@ -8,7 +8,7 @@ export function summarizeDeliveries(deliveries) {
|
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
10
|
export function runVerdict(run) {
|
|
11
|
-
const presented = workflowRunPresentation(run.status, run.pauseReason);
|
|
11
|
+
const presented = workflowRunPresentation(run.status, run.pauseReason, isWorkerStoppedPark(run));
|
|
12
12
|
const delivery = summarizeDeliveries(run.deliveries) ?? run.deliverySummary ?? undefined;
|
|
13
13
|
if ((presented.status === "succeeded" || presented.status === "succeeded-with-warnings") &&
|
|
14
14
|
(run.counts?.failed ?? 0) > 0 &&
|