hyper-animator-codex 0.8.0 → 0.8.3
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/README.md +30 -13
- package/bin/hyper-animator-codex.mjs +4 -4
- package/lib/install-skill.mjs +49 -20
- package/lib/minimax-config.mjs +5 -9
- package/package.json +10 -2
- package/skills/hyper-animator-codex/SKILL.md +33 -25
- package/skills/hyper-animator-codex/agents/openai.yaml +2 -2
- package/skills/hyper-animator-codex/contracts/shot-plan.schema.json +113 -7
- package/skills/hyper-animator-codex/contracts/workflow-policy.json +21 -3
- package/skills/hyper-animator-codex/references/examples/shot-plan-dual-catalog.json +41 -5
- package/skills/hyper-animator-codex/references/hyperframes-intent-workflow.md +20 -10
- package/skills/hyper-animator-codex/references/minimax-music-workflow.md +5 -7
- package/skills/hyper-animator-codex/references/narration-audio-workflow.md +20 -14
- package/skills/hyper-animator-codex/references/preview-controls-workflow.md +4 -2
- package/skills/hyper-animator-codex/references/shotcraft/motion-pack/README.md +32 -0
- package/skills/hyper-animator-codex/references/shotcraft/motion-pack/shotcraft-motion-pack.js +178 -0
- package/skills/hyper-animator-codex/scripts/generate_minimax_music.mjs +11 -6
- package/skills/hyper-animator-codex/scripts/generate_minimax_tts.mjs +20 -8
- package/skills/hyper-animator-codex/scripts/git_checkpoint.mjs +29 -3
- package/skills/hyper-animator-codex/scripts/inject_preview_controls.mjs +37 -1
- package/skills/hyper-animator-codex/scripts/minimax_runtime_config.mjs +5 -9
- package/skills/hyper-animator-codex/scripts/query_shotcraft.mjs +10 -1
- package/skills/hyper-animator-codex/scripts/shot_plan_schema_validator.mjs +3 -0
- package/skills/hyper-animator-codex/scripts/validate_hyperframes_html.py +122 -36
- package/skills/hyper-animator-codex/scripts/validate_shot_plan.mjs +110 -17
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { readFile } from "node:fs/promises";
|
|
3
4
|
import { dirname, join } from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import validateSchema, { schemaSha256 } from "./shot_plan_schema_validator.mjs";
|
|
5
7
|
|
|
6
8
|
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
7
9
|
const skillRoot = dirname(scriptDir);
|
|
10
|
+
const WORKFLOW_STAGES = new Set(["visual_draft", "audio_integrated", "render_ready"]);
|
|
11
|
+
const AUDIO_MODES = new Set(["generated", "user_local_file", "none", "deferred"]);
|
|
12
|
+
const ENERGY_LEVELS = new Set(["quiet", "balanced", "high", "cinematic"]);
|
|
13
|
+
const MOTION_PRESETS = new Set([
|
|
14
|
+
"none",
|
|
15
|
+
"slam-entrance",
|
|
16
|
+
"whip-transition",
|
|
17
|
+
"crash-zoom",
|
|
18
|
+
"push-stack-wipe",
|
|
19
|
+
"dataviz-landscape-open",
|
|
20
|
+
"panel-grid-reflow",
|
|
21
|
+
"brand-resolve",
|
|
22
|
+
]);
|
|
23
|
+
const SHOTCRAFT_MOTION_PRESETS = new Map([
|
|
24
|
+
["slam-entrance-moves", "slam-entrance"],
|
|
25
|
+
["shot-transitions", "whip-transition"],
|
|
26
|
+
["crash-zoom-punch", "crash-zoom"],
|
|
27
|
+
["bottom-push-stack-wipe", "push-stack-wipe"],
|
|
28
|
+
["dataviz-landscape-open", "dataviz-landscape-open"],
|
|
29
|
+
["panel-grid-moves", "panel-grid-reflow"],
|
|
30
|
+
["ui-to-brand-morph", "brand-resolve"],
|
|
31
|
+
]);
|
|
8
32
|
|
|
9
33
|
function isObject(value) {
|
|
10
34
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -16,8 +40,8 @@ function validateAudioTrack(track, label, errors) {
|
|
|
16
40
|
return;
|
|
17
41
|
}
|
|
18
42
|
|
|
19
|
-
if (!
|
|
20
|
-
errors.push(`audio.${label}.mode must be generated, user_local_file, or
|
|
43
|
+
if (!AUDIO_MODES.has(track.mode)) {
|
|
44
|
+
errors.push(`audio.${label}.mode must be generated, user_local_file, none, or deferred`);
|
|
21
45
|
}
|
|
22
46
|
if (track.mode === "generated" && track.provider !== "minimax_cn") {
|
|
23
47
|
errors.push(`audio.${label}.provider must be minimax_cn when mode is generated`);
|
|
@@ -25,8 +49,51 @@ function validateAudioTrack(track, label, errors) {
|
|
|
25
49
|
if (track.mode === "user_local_file" && (typeof track.path !== "string" || !track.path.trim())) {
|
|
26
50
|
errors.push(`audio.${label}.path is required when mode is user_local_file`);
|
|
27
51
|
}
|
|
28
|
-
if (track.mode === "none" && track.provider !== undefined) {
|
|
29
|
-
errors.push(`audio.${label}.provider must be omitted when mode is
|
|
52
|
+
if ((track.mode === "none" || track.mode === "deferred") && track.provider !== undefined) {
|
|
53
|
+
errors.push(`audio.${label}.provider must be omitted when mode is ${track.mode}`);
|
|
54
|
+
}
|
|
55
|
+
if ((track.mode === "none" || track.mode === "deferred") && track.path !== undefined) {
|
|
56
|
+
errors.push(`audio.${label}.path must be omitted when mode is ${track.mode}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function validateMotionContract(contract, label, shotcraftRefs, errors) {
|
|
61
|
+
if (!isObject(contract)) {
|
|
62
|
+
errors.push(`${label}.motion_contract is required`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!MOTION_PRESETS.has(contract.motion_preset)) {
|
|
67
|
+
errors.push(`${label}.motion_contract.motion_preset must be one of ${[...MOTION_PRESETS].join(", ")}`);
|
|
68
|
+
}
|
|
69
|
+
const expectedPresets = shotcraftRefs
|
|
70
|
+
.map((reference) => SHOTCRAFT_MOTION_PRESETS.get(reference))
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
if (new Set(expectedPresets).size > 1) {
|
|
73
|
+
errors.push(`${label}.shotcraft_refs require multiple motion presets; separate them into distinct shots`);
|
|
74
|
+
}
|
|
75
|
+
if (expectedPresets.length > 0 && !expectedPresets.includes(contract.motion_preset)) {
|
|
76
|
+
errors.push(`${label}.motion_contract.motion_preset must use ${expectedPresets.join(" or ")} for selected ShotCraft refs`);
|
|
77
|
+
}
|
|
78
|
+
if (!ENERGY_LEVELS.has(contract.energy)) {
|
|
79
|
+
errors.push(`${label}.motion_contract.energy must be quiet, balanced, high, or cinematic`);
|
|
80
|
+
}
|
|
81
|
+
for (const field of ["camera", "transition"]) {
|
|
82
|
+
if (typeof contract[field] !== "string" || !contract[field].trim()) {
|
|
83
|
+
errors.push(`${label}.motion_contract.${field} is required`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(contract.shotcraft_commitments) || contract.shotcraft_commitments.length === 0) {
|
|
87
|
+
errors.push(`${label}.motion_contract.shotcraft_commitments must contain at least one item`);
|
|
88
|
+
} else {
|
|
89
|
+
contract.shotcraft_commitments.forEach((commitment, index) => {
|
|
90
|
+
if (typeof commitment !== "string" || !commitment.trim()) {
|
|
91
|
+
errors.push(`${label}.motion_contract.shotcraft_commitments[${index}] must be a non-empty string`);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (!Number.isFinite(contract.readable_rest_seconds) || contract.readable_rest_seconds < 0) {
|
|
96
|
+
errors.push(`${label}.motion_contract.readable_rest_seconds must be zero or greater`);
|
|
30
97
|
}
|
|
31
98
|
}
|
|
32
99
|
|
|
@@ -34,7 +101,10 @@ function validateShotPlan(plan, catalogs) {
|
|
|
34
101
|
const errors = [];
|
|
35
102
|
|
|
36
103
|
if (!isObject(plan)) return ["plan must be a JSON object"];
|
|
37
|
-
if (plan.schema_version !== "1.
|
|
104
|
+
if (plan.schema_version !== "1.1.0") errors.push("schema_version must be 1.1.0");
|
|
105
|
+
if (!WORKFLOW_STAGES.has(plan.workflow_stage)) {
|
|
106
|
+
errors.push("workflow_stage must be visual_draft, audio_integrated, or render_ready");
|
|
107
|
+
}
|
|
38
108
|
if (!["assemble_existing_catalog_items", "generate_new_hyperframes_html"].includes(plan.generation_mode)) {
|
|
39
109
|
errors.push("generation_mode is invalid");
|
|
40
110
|
}
|
|
@@ -56,18 +126,26 @@ function validateShotPlan(plan, catalogs) {
|
|
|
56
126
|
} else {
|
|
57
127
|
validateAudioTrack(plan.audio.narration, "narration", errors);
|
|
58
128
|
validateAudioTrack(plan.audio.bgm, "bgm", errors);
|
|
129
|
+
if (plan.workflow_stage === "visual_draft" && plan.audio.narration?.mode === "generated") {
|
|
130
|
+
errors.push("audio.narration must not be generated before visual confirmation");
|
|
131
|
+
}
|
|
132
|
+
if (plan.workflow_stage !== "visual_draft" && plan.audio.narration?.mode === "deferred") {
|
|
133
|
+
errors.push("audio.narration.mode cannot stay deferred after visual confirmation");
|
|
134
|
+
}
|
|
59
135
|
}
|
|
60
136
|
|
|
61
137
|
if (!isObject(plan.model_routing)) {
|
|
62
138
|
errors.push("model_routing must be an object");
|
|
63
139
|
} else {
|
|
64
|
-
|
|
65
|
-
|
|
140
|
+
const authoring = plan.model_routing.page_animation_authoring;
|
|
141
|
+
const planning = plan.model_routing.planning_and_validation;
|
|
142
|
+
if (!isObject(authoring) || authoring.tier !== "high_quality" || !authoring.actual_model) {
|
|
143
|
+
errors.push("model_routing.page_animation_authoring must declare high_quality tier and actual_model");
|
|
66
144
|
}
|
|
67
|
-
if (!
|
|
68
|
-
errors.push("model_routing.planning_and_validation
|
|
69
|
-
} else if (
|
|
70
|
-
errors.push("model_routing.planning_and_validation must
|
|
145
|
+
if (!isObject(planning) || planning.tier !== "fast" || !planning.actual_model) {
|
|
146
|
+
errors.push("model_routing.planning_and_validation must declare fast tier and actual_model");
|
|
147
|
+
} else if (authoring?.actual_model === planning.actual_model) {
|
|
148
|
+
errors.push("model_routing.planning_and_validation must use a different, fast model");
|
|
71
149
|
}
|
|
72
150
|
}
|
|
73
151
|
|
|
@@ -102,6 +180,9 @@ function validateShotPlan(plan, catalogs) {
|
|
|
102
180
|
if (!Array.isArray(shot.hyperframes_refs)) {
|
|
103
181
|
errors.push(`${label}.hyperframes_refs must be an array`);
|
|
104
182
|
} else {
|
|
183
|
+
if (shot.hyperframes_refs.length === 0) {
|
|
184
|
+
errors.push(`${label}.hyperframes_refs must contain at least one HyperFrames reference`);
|
|
185
|
+
}
|
|
105
186
|
for (const reference of shot.hyperframes_refs) {
|
|
106
187
|
if (!catalogs.hyperframes.has(reference)) {
|
|
107
188
|
errors.push(`${label} has unknown HyperFrames reference: ${reference}`);
|
|
@@ -111,17 +192,16 @@ function validateShotPlan(plan, catalogs) {
|
|
|
111
192
|
if (!Array.isArray(shot.shotcraft_refs)) {
|
|
112
193
|
errors.push(`${label}.shotcraft_refs must be an array`);
|
|
113
194
|
} else {
|
|
195
|
+
if (shot.shotcraft_refs.length === 0) {
|
|
196
|
+
errors.push(`${label}.shotcraft_refs must contain at least one ShotCraft reference`);
|
|
197
|
+
}
|
|
114
198
|
for (const reference of shot.shotcraft_refs) {
|
|
115
199
|
if (!catalogs.shotcraft.has(reference)) {
|
|
116
200
|
errors.push(`${label} has unknown ShotCraft reference: ${reference}`);
|
|
117
201
|
}
|
|
118
202
|
}
|
|
119
203
|
}
|
|
120
|
-
|
|
121
|
-
&& Array.isArray(shot.shotcraft_refs)
|
|
122
|
-
&& shot.hyperframes_refs.length + shot.shotcraft_refs.length === 0) {
|
|
123
|
-
errors.push(`${label} must reference at least one catalog`);
|
|
124
|
-
}
|
|
204
|
+
validateMotionContract(shot.motion_contract, label, Array.isArray(shot.shotcraft_refs) ? shot.shotcraft_refs : [], errors);
|
|
125
205
|
|
|
126
206
|
if (index === 0 && shot.start_seconds !== 0) {
|
|
127
207
|
errors.push("shots must start at 0 seconds");
|
|
@@ -158,6 +238,14 @@ async function main() {
|
|
|
158
238
|
return;
|
|
159
239
|
}
|
|
160
240
|
const plan = JSON.parse(await readFile(path, "utf8"));
|
|
241
|
+
const schemaText = await readFile(
|
|
242
|
+
join(skillRoot, "contracts", "shot-plan.schema.json"),
|
|
243
|
+
"utf8",
|
|
244
|
+
);
|
|
245
|
+
const actualSchemaSha256 = createHash("sha256").update(schemaText).digest("hex");
|
|
246
|
+
if (actualSchemaSha256 !== schemaSha256) {
|
|
247
|
+
throw new Error("shot-plan schema validator is stale; run npm run build:contracts");
|
|
248
|
+
}
|
|
161
249
|
const hyperframesCatalog = JSON.parse(await readFile(
|
|
162
250
|
join(skillRoot, "references", "hyperframes-catalog-map.json"),
|
|
163
251
|
"utf8",
|
|
@@ -173,7 +261,12 @@ async function main() {
|
|
|
173
261
|
...card.styles.map((style) => style.key),
|
|
174
262
|
])),
|
|
175
263
|
};
|
|
176
|
-
const
|
|
264
|
+
const schemaErrors = validateSchema(plan)
|
|
265
|
+
? []
|
|
266
|
+
: validateSchema.errors.map((error) => (
|
|
267
|
+
`${error.instancePath || "/"} ${error.message}${error.params?.additionalProperty ? `: ${error.params.additionalProperty}` : ""}`
|
|
268
|
+
));
|
|
269
|
+
const errors = [...schemaErrors, ...validateShotPlan(plan, catalogs)];
|
|
177
270
|
if (errors.length > 0) {
|
|
178
271
|
output({ ok: false, path, errors }, 1);
|
|
179
272
|
return;
|