@officexapp/vidfarm-devcli 0.21.55 → 0.21.57
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/.agents/skills/vidfarm/SKILL.md +87 -1
- package/.agents/skills/vidfarm/harnesses/README.md +1 -0
- package/.agents/skills/vidfarm/references/automation-and-local-dev.md +6 -1
- package/.agents/skills/vidfarm/references/reviewing-renders.md +11 -2
- package/SKILL.director.md +104 -4
- package/SKILL.md +77 -5
- package/clipper.md +36 -4
- package/dist/src/cli.js +168 -4
- package/dist/src/devcli/clipper-run.js +941 -0
- package/dist/src/devcli/doctor.js +23 -1
- package/dist/src/devcli/local-frontend-server.js +10 -2
- package/dist/src/devcli/local-render.js +28 -2
- package/dist/src/devcli/marketplace-gigs.js +545 -39
- package/dist/src/devcli/qa-check.js +27 -1
- package/dist/src/devcli/update-check.js +340 -0
- package/dist/src/lib/engine-globals.js +138 -0
- package/dist/src/lib/frozen-render.js +130 -0
- package/dist/src/services/composition-lint.js +16 -0
- package/experimental/engaging-chat-convo.md +1370 -0
- package/experimental/flash-harness.md +549 -0
- package/marketplace.md +544 -14
- package/package.json +7 -2
- package/public/serve-shells/editor.html +27 -0
- package/public/serve-shells/library-files.html +28 -1
- package/public/serve-shells/library-raws.html +27 -0
- package/public/serve-shells/tools-clipper.html +27 -0
- package/public/serve-shells/tools-image.html +27 -0
- package/public/serve-shells/tools-video.html +27 -0
- package/update.md +27 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// else prints as ⚠/✗ but does not fail the command, so agents can run doctor
|
|
8
8
|
// unconditionally at session start.
|
|
9
9
|
import { spawnSync } from "node:child_process";
|
|
10
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
10
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
11
11
|
import { createRequire } from "node:module";
|
|
12
12
|
import net from "node:net";
|
|
13
13
|
import { homedir } from "node:os";
|
|
@@ -143,6 +143,28 @@ export async function runDoctorCommand(argv) {
|
|
|
143
143
|
const killOrphans = Boolean(parsed.values["kill-orphans"]);
|
|
144
144
|
const checks = [];
|
|
145
145
|
const add = (name, level, detail) => checks.push({ name, level, detail });
|
|
146
|
+
// 0. Are the two halves current, and are they the SAME version? The skill and
|
|
147
|
+
// the devcli ship on one semver, and a gap between them is the usual cause of
|
|
148
|
+
// "that command doesn't exist" — so it belongs in the health check, not only
|
|
149
|
+
// in the update runbook. Never fails the doctor: offline is not a defect, and
|
|
150
|
+
// a pending update is information, not breakage.
|
|
151
|
+
try {
|
|
152
|
+
const { installedDevcliVersion, installedSkillPath, readSkillVersion, compareSemver, readUpdateState, hoursSinceLastCheck } = await import("./update-check.js");
|
|
153
|
+
const devcli = installedDevcliVersion();
|
|
154
|
+
const skillPath = installedSkillPath();
|
|
155
|
+
const skill = skillPath ? readSkillVersion(readFileSync(skillPath, "utf8")) : null;
|
|
156
|
+
const since = hoursSinceLastCheck(readUpdateState());
|
|
157
|
+
const staleNote = since === null ? " · never checked for updates" : since >= 24 ? ` · last update check ${Math.round(since / 24)}d ago` : "";
|
|
158
|
+
if (devcli && skill && compareSemver(devcli, skill) !== 0) {
|
|
159
|
+
add("versions", "warn", `devcli ${devcli} vs skill ${skill} — MISMATCHED. They ship together; run: vidfarm update-check`);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
add("versions", since !== null && since < 24 ? "ok" : "warn", `devcli ${devcli ?? "?"} · skill ${skill ?? "not installed"}${staleNote}${staleNote ? " — vidfarm update-check" : ""}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// A version probe must never take the doctor down with it.
|
|
167
|
+
}
|
|
146
168
|
// 1. Node version.
|
|
147
169
|
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
148
170
|
add("node", nodeMajor >= 22 ? "ok" : "fail", `v${process.versions.node}${nodeMajor >= 22 ? "" : " — vidfarm-devcli needs Node >= 22"}`);
|
|
@@ -560,7 +560,7 @@ async function startLocalRender(input) {
|
|
|
560
560
|
void (async () => {
|
|
561
561
|
try {
|
|
562
562
|
const { renderCompositionLocally } = await import("./local-render.js");
|
|
563
|
-
await renderCompositionLocally({
|
|
563
|
+
const outcome = await renderCompositionLocally({
|
|
564
564
|
compositionHtml: compositionHtml,
|
|
565
565
|
outputPath,
|
|
566
566
|
stdio: "capture"
|
|
@@ -569,6 +569,11 @@ async function startLocalRender(input) {
|
|
|
569
569
|
record.outputPath = outputPath;
|
|
570
570
|
record.endedAt = Date.now();
|
|
571
571
|
console.log(`[vidfarm] local render ${renderId} finished (${((record.endedAt - record.startedAt) / 1000).toFixed(1)}s) → ${outputPath}`);
|
|
572
|
+
// The editor keeps the file (a still card is a legal output here), but a
|
|
573
|
+
// frozen render is almost always a bug and must not pass silently.
|
|
574
|
+
if (outcome.motion?.frozen) {
|
|
575
|
+
console.warn(`[vidfarm] ⚠ render ${renderId} NEVER MOVES — ${outcome.motion.reason} Check for window.__player/__hf assignments and a registered window.__timelines entry (vidfarm lint).`);
|
|
576
|
+
}
|
|
572
577
|
}
|
|
573
578
|
catch (error) {
|
|
574
579
|
record.status = "FAILED";
|
|
@@ -638,7 +643,7 @@ async function startStudioRender(input) {
|
|
|
638
643
|
void (async () => {
|
|
639
644
|
try {
|
|
640
645
|
const { renderCompositionLocally } = await import("./local-render.js");
|
|
641
|
-
await renderCompositionLocally({
|
|
646
|
+
const outcome = await renderCompositionLocally({
|
|
642
647
|
compositionHtml,
|
|
643
648
|
outputPath,
|
|
644
649
|
fps,
|
|
@@ -650,6 +655,9 @@ async function startStudioRender(input) {
|
|
|
650
655
|
job.stage = "rendering";
|
|
651
656
|
}
|
|
652
657
|
});
|
|
658
|
+
if (outcome.motion?.frozen) {
|
|
659
|
+
console.warn(`[vidfarm] ⚠ studio render ${job.filename} NEVER MOVES — ${outcome.motion.reason} Check for window.__player/__hf assignments and a registered window.__timelines entry (vidfarm lint).`);
|
|
660
|
+
}
|
|
653
661
|
// A cancel marks the job terminal for the UI but cannot abort the child
|
|
654
662
|
// process — don't resurrect a cancelled job when it eventually finishes.
|
|
655
663
|
if (job.status === "rendering") {
|
|
@@ -20,6 +20,8 @@ import os from "node:os";
|
|
|
20
20
|
import path from "node:path";
|
|
21
21
|
import { prepareProjectMediaForRender } from "../lib/render-media-prep.js";
|
|
22
22
|
import { resolveBundledFfprobe } from "../lib/ffprobe-path.js";
|
|
23
|
+
import { describeEngineGlobalHits, findEngineOwnedGlobalAssignments } from "../lib/engine-globals.js";
|
|
24
|
+
import { checkRenderMotion } from "../lib/frozen-render.js";
|
|
23
25
|
import { runHyperframesCommand } from "./hyperframes-cli.js";
|
|
24
26
|
import { normalizeTikTokCaptionLayout } from "./composition-edit.js";
|
|
25
27
|
// Same invariant as the backend's forceEvenCompositionDimensions: libx264
|
|
@@ -70,6 +72,16 @@ export async function renderCompositionLocally(input) {
|
|
|
70
72
|
if (!input.compositionHtml.includes("data-composition-id=")) {
|
|
71
73
|
throw new Error("Local render requires composition HTML with data-composition-id.");
|
|
72
74
|
}
|
|
75
|
+
// ── Preflight: engine-owned globals ─────────────────────────────────────────
|
|
76
|
+
// Refuse BEFORE spending 45 seconds producing a still image. This is the one
|
|
77
|
+
// authoring mistake whose render "succeeds" in every observable way, so the
|
|
78
|
+
// only place it can be stopped is here.
|
|
79
|
+
if (!input.allowEngineGlobals) {
|
|
80
|
+
const hits = findEngineOwnedGlobalAssignments(input.compositionHtml);
|
|
81
|
+
if (hits.length > 0) {
|
|
82
|
+
throw new Error(`Refusing to render: ${describeEngineGlobalHits(hits)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
73
85
|
const startedAt = Date.now();
|
|
74
86
|
const projectDir = await mkdtemp(path.join(os.tmpdir(), "vidfarm-local-render-"));
|
|
75
87
|
let prepNotes = [];
|
|
@@ -121,13 +133,27 @@ export async function renderCompositionLocally(input) {
|
|
|
121
133
|
if (!existsSync(input.outputPath)) {
|
|
122
134
|
throw new Error(`hyperframes render reported success but wrote no file at ${input.outputPath}`);
|
|
123
135
|
}
|
|
136
|
+
// ── Post-render: does it actually move? ──────────────────────────────────
|
|
137
|
+
// hyperframes downgrades `sub_timeline_readiness_timeout` to a warning and
|
|
138
|
+
// writes the file anyway, so a correct-looking exit 0 is not evidence of a
|
|
139
|
+
// video. One cheap ffmpeg pass settles it. Never throws — a missing ffmpeg
|
|
140
|
+
// comes back `skipped` rather than failing a render that is probably fine.
|
|
141
|
+
let motion;
|
|
142
|
+
if (!input.skipMotionCheck) {
|
|
143
|
+
motion = await checkRenderMotion(input.outputPath);
|
|
144
|
+
if (motion.frozen)
|
|
145
|
+
log("local_render.frozen", { output: input.outputPath, ...motion });
|
|
146
|
+
else if (motion.skipped)
|
|
147
|
+
log("local_render.motion_unverified", { reason: motion.reason });
|
|
148
|
+
}
|
|
124
149
|
const durationMs = Date.now() - startedAt;
|
|
125
|
-
log("local_render.succeeded", { output: input.outputPath, duration_ms: durationMs });
|
|
150
|
+
log("local_render.succeeded", { output: input.outputPath, duration_ms: durationMs, frozen: motion?.frozen ?? null });
|
|
126
151
|
return {
|
|
127
152
|
outputPath: input.outputPath,
|
|
128
153
|
projectDir: input.keepProjectDir ? projectDir : null,
|
|
129
154
|
durationMs,
|
|
130
|
-
prepNotes
|
|
155
|
+
prepNotes,
|
|
156
|
+
...(motion ? { motion } : {})
|
|
131
157
|
};
|
|
132
158
|
}
|
|
133
159
|
finally {
|