@vosjs/cli 0.80.0 → 0.81.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/README.md +22 -8
- package/dist/{assetManifest-ETT3C7ZA.js → assetManifest-27NKSS7N.js} +2 -2
- package/dist/{chunk-PJ6MEJZV.js → chunk-352AC3V6.js} +37 -9
- package/dist/chunk-352AC3V6.js.map +1 -0
- package/dist/{chunk-SP5OHTVB.js → chunk-S3RI2SHH.js} +563 -250
- package/dist/chunk-S3RI2SHH.js.map +1 -0
- package/dist/cli.js +4 -4
- package/dist/index.d.ts +27 -27
- package/dist/index.js +2 -2
- package/dist/{run-2ELHMPTB.js → run-MEDD2Q74.js} +7 -3
- package/dist/{shotList-MSNWLVDN.js → shotList-PL7XP6AC.js} +6 -1
- package/dist/shotList-PL7XP6AC.js.map +1 -0
- package/package.json +4 -4
- package/schema/actions.schema.json +56 -45
- package/schema/doc.schema.json +27 -1
- package/skills/VERSION +1 -1
- package/skills/launch-kit/SKILL.md +7 -0
- package/skills/product-video/SKILL.md +91 -3
- package/skills/product-video/references/how-to.md +78 -0
- package/skills/product-video/references/taste.md +4 -0
- package/skills/product-video/references/troubleshooting.md +8 -4
- package/skills/vos-cut/SKILL.md +34 -0
- package/dist/chunk-PJ6MEJZV.js.map +0 -1
- package/dist/chunk-SP5OHTVB.js.map +0 -1
- package/dist/shotList-MSNWLVDN.js.map +0 -1
- /package/dist/{assetManifest-ETT3C7ZA.js.map → assetManifest-27NKSS7N.js.map} +0 -0
- /package/dist/{run-2ELHMPTB.js.map → run-MEDD2Q74.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -839,7 +839,7 @@ async function cmdCheck(argv) {
|
|
|
839
839
|
const result = runCheck(parsed);
|
|
840
840
|
if (result.config) {
|
|
841
841
|
const baseDir = baseDirOf(source);
|
|
842
|
-
const { hostedLiteralWarnings } = await import("./assetManifest-
|
|
842
|
+
const { hostedLiteralWarnings } = await import("./assetManifest-27NKSS7N.js");
|
|
843
843
|
const extra = [
|
|
844
844
|
...baseDir ? missingManifestFiles(result.config, baseDir) : [],
|
|
845
845
|
...hostedLiteralWarnings(result.config)
|
|
@@ -873,7 +873,7 @@ async function cmdCheck(argv) {
|
|
|
873
873
|
return result.ok ? EXIT_OK : EXIT_ERROR;
|
|
874
874
|
}
|
|
875
875
|
async function delegate(argv) {
|
|
876
|
-
const { run } = await import("./run-
|
|
876
|
+
const { run } = await import("./run-MEDD2Q74.js");
|
|
877
877
|
return await run(argv);
|
|
878
878
|
}
|
|
879
879
|
async function printHelp() {
|
|
@@ -907,7 +907,7 @@ async function main() {
|
|
|
907
907
|
const engine = HELP_ENGINE.split("\n").filter(
|
|
908
908
|
(l) => l.startsWith(` vos ${cmd} `)
|
|
909
909
|
);
|
|
910
|
-
const { verbHelp } = await import("./run-
|
|
910
|
+
const { verbHelp } = await import("./run-MEDD2Q74.js");
|
|
911
911
|
const take = verbHelp(cmd);
|
|
912
912
|
process.stdout.write(
|
|
913
913
|
`${engine.join("\n")}
|
|
@@ -935,7 +935,7 @@ ${take.includes("no such verb") ? "" : take}`
|
|
|
935
935
|
main().then(async (code) => {
|
|
936
936
|
if (code === EXIT_OK) {
|
|
937
937
|
const verb = process.argv[2] ?? "";
|
|
938
|
-
const { HELP } = await import("./run-
|
|
938
|
+
const { HELP } = await import("./run-MEDD2Q74.js");
|
|
939
939
|
const documented = [
|
|
940
940
|
...helpFlagsFor(HELP_ENGINE, verb),
|
|
941
941
|
...helpFlagsFor(HELP, verb)
|
package/dist/index.d.ts
CHANGED
|
@@ -137,33 +137,6 @@ type RecordOpts = Omit<RecordOpts$1, 'context' | 'env' | 'platform' | 'outputs'
|
|
|
137
137
|
};
|
|
138
138
|
declare function recordTake(browser: Browser, url: string, actions: ActionsFile, paths: TakePaths, log: (msg: string) => void, opts?: RecordOpts): Promise<_vosjs_render_core_record.RecordResult>;
|
|
139
139
|
|
|
140
|
-
/** Entry point — the delegation contract for @vosjs/cli: the host forwards
|
|
141
|
-
every non-engine verb here (`vos <verb>`). The exported `manifest` (index.ts)
|
|
142
|
-
tells the host which verbs exist and how to summarize them in help. */
|
|
143
|
-
declare function run(argv: string[]): Promise<number>;
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* The verb manifest of the take pipeline and the vos.so verbs — what
|
|
147
|
-
* `vos help` lists under the engine verbs. It kept the shape a separately
|
|
148
|
-
* installed plugin once handed the host (name + host range), so a script
|
|
149
|
-
* reading it keeps working.
|
|
150
|
-
*/
|
|
151
|
-
interface PluginVerb {
|
|
152
|
-
name: string;
|
|
153
|
-
summary: string;
|
|
154
|
-
}
|
|
155
|
-
interface PluginManifest {
|
|
156
|
-
name: string;
|
|
157
|
-
/** Host versions this plugin speaks the run(argv) contract with. */
|
|
158
|
-
hostRange: string;
|
|
159
|
-
verbs: PluginVerb[];
|
|
160
|
-
}
|
|
161
|
-
declare const manifest: PluginManifest;
|
|
162
|
-
|
|
163
|
-
declare function encodeRecording(browser: Browser, takeDir: string, onProgress: (fraction: number) => void): Promise<{
|
|
164
|
-
bytes: number;
|
|
165
|
-
}>;
|
|
166
|
-
|
|
167
140
|
/** The release's words a plan writes into the cut's titles and end card. */
|
|
168
141
|
interface ReleaseWords {
|
|
169
142
|
headline?: string | null;
|
|
@@ -316,6 +289,33 @@ interface PlanOptions {
|
|
|
316
289
|
}
|
|
317
290
|
declare function planTake(dir: string, opts?: PlanOptions): Promise<PlanSummary>;
|
|
318
291
|
|
|
292
|
+
/** Entry point — the delegation contract for @vosjs/cli: the host forwards
|
|
293
|
+
every non-engine verb here (`vos <verb>`). The exported `manifest` (index.ts)
|
|
294
|
+
tells the host which verbs exist and how to summarize them in help. */
|
|
295
|
+
declare function run(argv: string[]): Promise<number>;
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* The verb manifest of the take pipeline and the vos.so verbs — what
|
|
299
|
+
* `vos help` lists under the engine verbs. It kept the shape a separately
|
|
300
|
+
* installed plugin once handed the host (name + host range), so a script
|
|
301
|
+
* reading it keeps working.
|
|
302
|
+
*/
|
|
303
|
+
interface PluginVerb {
|
|
304
|
+
name: string;
|
|
305
|
+
summary: string;
|
|
306
|
+
}
|
|
307
|
+
interface PluginManifest {
|
|
308
|
+
name: string;
|
|
309
|
+
/** Host versions this plugin speaks the run(argv) contract with. */
|
|
310
|
+
hostRange: string;
|
|
311
|
+
verbs: PluginVerb[];
|
|
312
|
+
}
|
|
313
|
+
declare const manifest: PluginManifest;
|
|
314
|
+
|
|
315
|
+
declare function encodeRecording(browser: Browser, takeDir: string, onProgress: (fraction: number) => void): Promise<{
|
|
316
|
+
bytes: number;
|
|
317
|
+
}>;
|
|
318
|
+
|
|
319
319
|
interface DigestOptions {
|
|
320
320
|
outDir?: string;
|
|
321
321
|
full?: number;
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
startTakeServer,
|
|
15
15
|
validateActions,
|
|
16
16
|
waitForPageDone
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-S3RI2SHH.js";
|
|
18
18
|
import {
|
|
19
19
|
BrowserUnavailableError,
|
|
20
20
|
configDuration,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
loadTake,
|
|
30
30
|
pullMedia,
|
|
31
31
|
takePaths
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-352AC3V6.js";
|
|
33
33
|
import {
|
|
34
34
|
apiError,
|
|
35
35
|
apiJson,
|
|
@@ -2,23 +2,27 @@
|
|
|
2
2
|
import {
|
|
3
3
|
HELP,
|
|
4
4
|
MULTI_FLAGS,
|
|
5
|
+
previousCutReport,
|
|
6
|
+
reRecord,
|
|
5
7
|
run,
|
|
6
8
|
strictFails,
|
|
7
9
|
takeBackdrop,
|
|
8
10
|
takeBrowserArgs,
|
|
9
11
|
verbHelp
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-S3RI2SHH.js";
|
|
11
13
|
import "./chunk-B373NVJE.js";
|
|
12
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-352AC3V6.js";
|
|
13
15
|
import "./chunk-J3KHIZCH.js";
|
|
14
16
|
import "./chunk-6XPFWQKP.js";
|
|
15
17
|
export {
|
|
16
18
|
HELP,
|
|
17
19
|
MULTI_FLAGS,
|
|
20
|
+
previousCutReport,
|
|
21
|
+
reRecord,
|
|
18
22
|
run,
|
|
19
23
|
strictFails,
|
|
20
24
|
takeBackdrop,
|
|
21
25
|
takeBrowserArgs,
|
|
22
26
|
verbHelp
|
|
23
27
|
};
|
|
24
|
-
//# sourceMappingURL=run-
|
|
28
|
+
//# sourceMappingURL=run-MEDD2Q74.js.map
|
|
@@ -143,6 +143,7 @@ function shotList(actions) {
|
|
|
143
143
|
// gestures the recorder adds around each ask: about a second a beat
|
|
144
144
|
aboutS: Math.round((total + beats.length * 1e3) / 1e3),
|
|
145
145
|
setup: actions.setup?.length ?? 0,
|
|
146
|
+
...actions.teardown?.length ? { teardown: actions.teardown.length } : {},
|
|
146
147
|
...peers.length ? { peers } : {}
|
|
147
148
|
};
|
|
148
149
|
}
|
|
@@ -173,6 +174,10 @@ function formatShotList(list) {
|
|
|
173
174
|
out.push(
|
|
174
175
|
"Move at a talking pace, let each screen settle before the next beat, and end on the finished state. Press the icon again to stop; the take opens in the studio and saves to your shelf."
|
|
175
176
|
);
|
|
177
|
+
if (list.teardown)
|
|
178
|
+
out.push(
|
|
179
|
+
`After you stop: the script's ${list.teardown} teardown step${list.teardown === 1 ? "" : "s"} (remove what the take created) are yours to do; they are not part of the take.`
|
|
180
|
+
);
|
|
176
181
|
return out.join("\n");
|
|
177
182
|
}
|
|
178
183
|
export {
|
|
@@ -182,4 +187,4 @@ export {
|
|
|
182
187
|
sayTarget,
|
|
183
188
|
shotList
|
|
184
189
|
};
|
|
185
|
-
//# sourceMappingURL=shotList-
|
|
190
|
+
//# sourceMappingURL=shotList-PL7XP6AC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin/shotList.ts"],"sourcesContent":["/**\n * The shot list: an `actions.json` said in words, for a person who will\n * record it by hand. The last rung of the session ladder is \"the human\n * records, the agent cuts\", and an agent that falls to it should hand over\n * more than an apology: the flow it worked out, as numbered beats with the\n * pace it wanted, so its knowledge survives the hop to a human. The take\n * comes back through the ordinary handoff and the agent cuts it.\n *\n * Pure: an ActionsFile in, lines out. Selectors are said as what they name\n * where the script says (an id, a caption, a text= locator), and as the\n * selector itself where it does not: a person reading `#filter-late` can\n * find it; a person reading nothing cannot.\n */\nimport type { ActionStep, ActionsFile } from './actions'\nimport { askedMs } from './pace'\nimport { pressSequence, splitChord } from '@vosjs/render-core/record'\n\n/** A key or a chord as a person reads it: `Mod` is ⌘ on a Mac, Ctrl elsewhere. */\nexport function sayChord(chord: string): string {\n const { mods, key } = splitChord(chord)\n const word = (k: string) =>\n k === 'Mod' || k === 'ControlOrMeta'\n ? '⌘/Ctrl'\n : k === 'Control'\n ? 'Ctrl'\n : k === 'Meta'\n ? '⌘'\n : k === 'Alt'\n ? 'Alt/Option'\n : k === ' '\n ? 'Space'\n : k\n return [...mods, key].map(word).join('+')\n}\n\n/** A viewport point as a person finds it: where it is, and the script's numbers. */\nfunction sayPoint(p: { x?: number; y?: number }): string {\n const x = p.x ?? 0\n const y = p.y ?? 0\n return `the spot at ${Math.round(x)}, ${Math.round(y)} (${y < 360 ? 'upper' : 'lower'} ${x < 640 ? 'left' : 'right'})`\n}\n\n/** A list in words: `a`, `a then b`, `a, b then c`. */\nconst thenList = (xs: string[]): string =>\n xs.length <= 1\n ? (xs[0] ?? '')\n : `${xs.slice(0, -1).join(', ')} then ${xs[xs.length - 1]}`\n\nexport interface ShotBeat {\n n: number\n /** what to do, in words */\n line: string\n /** how long to hold after, in seconds, when the script asks for a hold */\n holdS?: number\n}\n\nexport interface ShotList {\n url?: string\n viewport?: { width: number; height: number }\n beats: ShotBeat[]\n /** the take's asked length, from the script's own pacing */\n aboutS: number\n setup: number\n /** The script's teardown steps: what the person undoes once they stop. */\n teardown?: number\n /**\n * What each other person does (the script's `peers`), as beats tied to\n * the take's own: a second pair of hands, in their own browser, on the\n * same page.\n */\n peers?: { id: string; lines: string[] }[]\n}\n\n/** A selector as a person reads it: the text it names, or the selector itself. */\nexport function sayTarget(selector: string): string {\n const text = /^text=(['\"]?)(.+?)\\1$/.exec(selector)\n if (text) return `\"${text[2]}\"`\n const has = /:has-text\\((['\"])(.+?)\\1\\)/.exec(selector)\n if (has) return `\"${has[2]}\"`\n const role = /getByRole\\(['\"](\\w+)['\"],\\s*\\{\\s*name:\\s*(['\"])(.+?)\\2/.exec(\n selector,\n )\n if (role) return `the ${role[1]} \"${role[3]}\"`\n const label = /\\[aria-label=(['\"])(.+?)\\1\\]/.exec(selector)\n if (label) return `\"${label[2]}\"`\n const ph = /\\[placeholder=(['\"])(.+?)\\1\\]/.exec(selector)\n if (ph) return `the \"${ph[2]}\" field`\n return `\\`${selector}\\``\n}\n\nconst s1 = (ms: number) => `${Math.round(ms / 100) / 10}`.replace(/\\.0$/, '')\n\n/** A step's id, said as a name when it reads as one (`late`, `new-order`, `openBilling`). */\nexport function sayId(id: string | undefined): string | null {\n if (!id || /^\\d+$/.test(id) || id.length < 3) return null\n const words = id\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .replace(/[-_]+/g, ' ')\n .toLowerCase()\n .trim()\n return words.length >= 3 ? words : null\n}\n\n/** What a step points at, for a person: the words the selector names, else the id's, else the selector. */\nfunction sayStepTarget(step: { selector: string; id?: string }): string {\n const bySelector = sayTarget(step.selector)\n if (!bySelector.startsWith('`')) return bySelector\n const byId = sayId(step.id)\n return byId ? `${byId} (${bySelector})` : bySelector\n}\n\n/** A page condition, for a person: what they should see before going on. */\nfunction sayCondition(c: {\n selector?: string\n text?: string\n gone?: string\n enabled?: boolean\n js?: string\n equals?: unknown\n id?: string\n}): string {\n // A state the app keeps in script: a person reads it in the browser's\n // console, so the beat names the expression to run there.\n if (c.js !== undefined)\n return c.equals !== undefined\n ? `the console's \\`${c.js}\\` gives ${JSON.stringify(c.equals)}`\n : `the console's \\`${c.js}\\` is true`\n if (c.text !== undefined) return `\"${c.text}\" shows on the page`\n if (c.gone !== undefined) return `${sayTarget(c.gone)} is gone`\n const target = sayStepTarget({ selector: c.selector ?? '', id: c.id })\n if (c.enabled === true) return `${target} is enabled`\n if (c.enabled === false) return `${target} is disabled`\n return `${target} is showing`\n}\n\nfunction beatOf(step: ActionStep): string | null {\n const line = gestureBeat(step)\n if (!line) return line\n // What the cut's camera does here, said where a person reads it: a step\n // marked as the beat's payoff is zoomed in on, one marked against it is not.\n const camera =\n step.zoom === true\n ? ' (zoomed in on in the cut)'\n : step.zoom === false\n ? ' (the cut stays wide here)'\n : ''\n // A step that spends something real is marked where a person reads it.\n return step.guard\n ? `${line}${camera} (this spends something real: do it only if you mean to)`\n : `${line}${camera}`\n}\n\nfunction gestureBeat(step: ActionStep): string | null {\n const cap = step.caption\n const named = cap ? `${cap}: ` : ''\n switch (step.do) {\n case 'expect':\n return `${named}Check that ${sayCondition(step)}`\n case 'await':\n return `${named}Wait until ${sayCondition({ ...step.until, id: step.id })} (off camera: pause the recording meanwhile)`\n case 'download':\n return step.selector !== undefined\n ? `${named}Download the file ${sayStepTarget({ selector: step.selector, id: step.id })} gives${step.as ? `, as ${step.as}` : ''}`\n : `${named}Download what ${sayTarget(step.src ?? '')} plays${step.as ? `, as ${step.as}` : ''}`\n case 'wait':\n return null\n case 'hover':\n return `${named}Rest the pointer on ${step.selector !== undefined ? sayStepTarget({ selector: step.selector, id: step.id }) : sayPoint(step)}`\n case 'click': {\n const verb =\n step.count === 3\n ? 'Triple-click'\n : step.count === 2\n ? 'Double-click'\n : 'Click'\n const button =\n step.button === 'right'\n ? ' with the right button'\n : step.button === 'middle'\n ? ' with the middle button'\n : ''\n const target =\n step.selector !== undefined\n ? sayStepTarget({ selector: step.selector, id: step.id })\n : sayPoint(step)\n const holding = step.modifiers?.length\n ? `Hold ${step.modifiers.map(sayChord).join('+')} and ${verb.toLowerCase()}`\n : verb\n return `${named}${holding} ${target}${button}`\n }\n case 'type':\n return `${named}${step.replace ? 'Replace what is in' : 'Type'}${step.replace ? ` ${sayStepTarget(step)} with \"${step.text}\"` : ` \"${step.text}\" into ${sayStepTarget(step)}`}${step.at === 'end' ? ', at the end' : step.at === 'start' ? ', at the start' : ''}`\n case 'press':\n return `${named}Press ${thenList(pressSequence(step).map(sayChord))}${step.selector ? ` in ${sayStepTarget(step as { selector: string; id?: string })}` : ''}`\n case 'paste': {\n const what =\n step.text !== undefined\n ? `\"${step.text.length > 60 ? `${step.text.slice(0, 57)}...` : step.text}\"`\n : `the file ${step.file}`\n return `${named}Copy ${what}, then paste it${step.selector ? ` into ${sayStepTarget({ selector: step.selector, id: step.id })}` : ''} (⌘/Ctrl+V)`\n }\n case 'scroll':\n return `${named}Scroll ${step.dy < 0 ? 'up' : 'down'} ${step.dy < 0 ? 'a little' : 'a little'}`\n case 'move':\n return `${named}Move the pointer to the ${step.y < 360 ? 'upper' : 'lower'} ${step.x < 640 ? 'left' : 'right'}`\n case 'drag': {\n const holding = step.modifiers?.length\n ? `Hold ${step.modifiers.map(sayChord).join('+')} and `\n : ''\n const verb = holding ? 'drag' : 'Drag'\n if (step.path !== undefined)\n return `${named}${holding}${holding ? 'draw' : 'Draw'} the stroke ${typeof step.path === 'string' ? `in ${step.path}` : `of ${step.path.length} points`}, in one press`\n return `${named}${holding}${verb} ${step.selector ? sayStepTarget(step as { selector: string; id?: string }) : 'from the start point'} across`\n }\n }\n}\n\nexport function shotList(actions: ActionsFile): ShotList {\n const beats: ShotBeat[] = []\n let pendingHold = 0\n let total = 0\n // Which beat a take step is (or, for a wait, the beat it holds), so a\n // peer's step can say when it happens.\n const beatOfId = new Map<string, number>()\n for (const step of actions.steps) {\n if (step.id)\n beatOfId.set(\n step.id,\n Math.max(1, beats.length + (step.do === 'wait' ? 0 : 1)),\n )\n total += askedMs(step)\n if (step.do === 'wait') {\n // A wait after a beat is that beat's hold; a leading wait is the\n // settle before the first, which a person does without being told.\n if (beats.length) {\n const last = beats[beats.length - 1]\n last.holdS = (last.holdS ?? 0) + step.ms / 1000\n } else pendingHold += step.ms\n continue\n }\n const line = beatOf(step)\n if (!line) continue\n const beat: ShotBeat = { n: beats.length + 1, line }\n if (step.do === 'hover') beat.holdS = askedMs(step) / 1000\n beats.push(beat)\n }\n void pendingHold\n const peers = (actions.peers ?? []).map((peer) => ({\n id: peer.id,\n lines: peer.steps\n .map((step) => {\n const line = step.do === 'wait' ? `Wait ${s1(step.ms)} s` : beatOf(step)\n if (!line) return null\n const when = step.with\n ? `As beat ${beatOfId.get(step.with) ?? '?'} starts: `\n : step.after\n ? `Once beat ${beatOfId.get(step.after) ?? '?'} is done: `\n : ''\n return `${when}${line}`\n })\n .filter((l): l is string => l !== null),\n }))\n return {\n url: actions.url,\n viewport: actions.viewport,\n beats,\n // gestures the recorder adds around each ask: about a second a beat\n aboutS: Math.round((total + beats.length * 1000) / 1000),\n setup: actions.setup?.length ?? 0,\n ...(actions.teardown?.length ? { teardown: actions.teardown.length } : {}),\n ...(peers.length ? { peers } : {}),\n }\n}\n\n/** The list as the person reads it. */\nexport function formatShotList(list: ShotList): string {\n const out: string[] = []\n out.push(\n `Record this with the vosso extension in your own signed-in browser, about ${list.aboutS} seconds${list.viewport ? `, window about ${list.viewport.width}×${list.viewport.height}` : ''}.`,\n )\n if (list.url)\n out.push(`Start on ${list.url}, signed in, with the page settled.`)\n if (list.setup)\n out.push(\n `Before you press record: the script's ${list.setup} setup step${list.setup === 1 ? '' : 's'} (sign in, dismiss banners) are yours to do first; they are not part of the take.`,\n )\n out.push('')\n for (const b of list.beats) {\n const hold =\n b.holdS && b.holdS >= 0.4 ? ` Hold ${s1(b.holdS * 1000)} s.` : ''\n out.push(`${b.n}. ${b.line}.${hold}`)\n }\n for (const peer of list.peers ?? []) {\n out.push('')\n out.push(\n `A second person (${peer.id}) does this at the same time, in their own browser on the same page; their screen is not recorded, the app shows what they do:`,\n )\n peer.lines.forEach((l, i) => out.push(` ${peer.id}${i + 1}. ${l}.`))\n }\n out.push('')\n out.push(\n 'Move at a talking pace, let each screen settle before the next beat, and end on the finished state. Press the icon again to stop; the take opens in the studio and saves to your shelf.',\n )\n if (list.teardown)\n out.push(\n `After you stop: the script's ${list.teardown} teardown step${list.teardown === 1 ? '' : 's'} (remove what the take created) are yours to do; they are not part of the take.`,\n )\n return out.join('\\n')\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vosjs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.1",
|
|
4
4
|
"description": "The vos CLI: record the real product from a scripted browser flow, auto-zoom from the cursor track, cut as data in doc.json, render deterministic video, deliver a release's video cuts per destination spec, and sync with vos.so. One binary, every verb, MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
"mediabunny": "^1.55.7",
|
|
53
53
|
"playwright": "^1.49.0",
|
|
54
54
|
"@vosjs/core": "^0.31.0",
|
|
55
|
-
"@vosjs/editor": "^1.6.0",
|
|
56
55
|
"@vosjs/elements": "^0.12.1",
|
|
57
|
-
"@vosjs/
|
|
56
|
+
"@vosjs/editor": "^1.6.0",
|
|
58
57
|
"@vosjs/shared": "^0.7.0",
|
|
59
|
-
"@vosjs/
|
|
58
|
+
"@vosjs/render-core": "^0.10.0",
|
|
59
|
+
"@vosjs/studio-core": "^0.46.0",
|
|
60
60
|
"@vosjs/timeline": "^0.5.0",
|
|
61
61
|
"@vosjs/tween": "^0.8.3"
|
|
62
62
|
},
|
|
@@ -33,51 +33,14 @@
|
|
|
33
33
|
"type": "array",
|
|
34
34
|
"description": "Run BEFORE the camera rolls, after the first navigation: a sign-in form, a cookie banner, an onboarding tour. Plain actions with no cursor, no frames, no pace and nothing in meta.steps; then the recorder opens `url` again and the take begins where the setup left it. A selector that never appears fails the take before anything is recorded (exit 2). A `type` step's text may be { \"env\": \"NAME\" }, read at run time and never logged or stored; a literal typed into a password field is refused, because actions.json is committed and pushed.",
|
|
35
35
|
"items": {
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"id": {
|
|
45
|
-
"type": "string"
|
|
46
|
-
},
|
|
47
|
-
"selector": {
|
|
48
|
-
"type": "string"
|
|
49
|
-
},
|
|
50
|
-
"text": {
|
|
51
|
-
"oneOf": [
|
|
52
|
-
{
|
|
53
|
-
"type": "string"
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"type": "object",
|
|
57
|
-
"required": ["env"],
|
|
58
|
-
"additionalProperties": false,
|
|
59
|
-
"properties": {
|
|
60
|
-
"env": {
|
|
61
|
-
"type": "string",
|
|
62
|
-
"description": "the environment variable holding the value"
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
]
|
|
67
|
-
},
|
|
68
|
-
"key": {
|
|
69
|
-
"type": "string",
|
|
70
|
-
"description": "press: a Playwright key name (Enter, Tab, Escape)"
|
|
71
|
-
},
|
|
72
|
-
"url": {
|
|
73
|
-
"type": "string",
|
|
74
|
-
"description": "goto: a page to open, for a sign-in that lives elsewhere"
|
|
75
|
-
},
|
|
76
|
-
"ms": {
|
|
77
|
-
"type": "number",
|
|
78
|
-
"description": "wait: the pause; click: the read after the page settled, held from its last visual change (default 150); type and scroll: the settle after (defaults 150/200)"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
36
|
+
"$ref": "#/$defs/offCameraStep"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"teardown": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"description": "Run AFTER the take, off camera, with the same verbs and rules as setup: what the take created in the product (an API key, a project, an invite) removed again, so a rehearsal or a re-record leaves the account as it found it. The steps run once the screencast has stopped, on the take's own page and session, after a --dry-run rehearsal too, and also when the take stops early or throws after its first step ran. Nothing of them is captured or written to meta.steps. Bounded at 60 s in all. A step that fails stops the teardown and is reported (the --json done event's `teardown`), and never fails the take or changes its exit code. A { \"env\": \"NAME\" } the shell does not hold refuses the take before it begins.",
|
|
42
|
+
"items": {
|
|
43
|
+
"$ref": "#/$defs/offCameraStep"
|
|
81
44
|
}
|
|
82
45
|
},
|
|
83
46
|
"ready": {
|
|
@@ -1107,6 +1070,54 @@
|
|
|
1107
1070
|
}
|
|
1108
1071
|
}
|
|
1109
1072
|
]
|
|
1073
|
+
},
|
|
1074
|
+
"offCameraStep": {
|
|
1075
|
+
"type": "object",
|
|
1076
|
+
"required": ["do"],
|
|
1077
|
+
"additionalProperties": false,
|
|
1078
|
+
"properties": {
|
|
1079
|
+
"do": {
|
|
1080
|
+
"type": "string",
|
|
1081
|
+
"enum": ["wait", "click", "type", "press", "goto"]
|
|
1082
|
+
},
|
|
1083
|
+
"id": {
|
|
1084
|
+
"type": "string"
|
|
1085
|
+
},
|
|
1086
|
+
"selector": {
|
|
1087
|
+
"type": "string"
|
|
1088
|
+
},
|
|
1089
|
+
"text": {
|
|
1090
|
+
"oneOf": [
|
|
1091
|
+
{
|
|
1092
|
+
"type": "string"
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
"type": "object",
|
|
1096
|
+
"required": ["env"],
|
|
1097
|
+
"additionalProperties": false,
|
|
1098
|
+
"properties": {
|
|
1099
|
+
"env": {
|
|
1100
|
+
"type": "string",
|
|
1101
|
+
"description": "the environment variable holding the value"
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
]
|
|
1106
|
+
},
|
|
1107
|
+
"key": {
|
|
1108
|
+
"type": "string",
|
|
1109
|
+
"description": "press: a Playwright key name (Enter, Tab, Escape)"
|
|
1110
|
+
},
|
|
1111
|
+
"url": {
|
|
1112
|
+
"type": "string",
|
|
1113
|
+
"description": "goto: a page to open, for a sign-in that lives elsewhere"
|
|
1114
|
+
},
|
|
1115
|
+
"ms": {
|
|
1116
|
+
"type": "number",
|
|
1117
|
+
"description": "wait: the pause; click: the read after the page settled, held from its last visual change (default 150); type and scroll: the settle after (defaults 150/200)"
|
|
1118
|
+
}
|
|
1119
|
+
},
|
|
1120
|
+
"description": "One setup or teardown step: a plain action, with no cursor and no frames."
|
|
1110
1121
|
}
|
|
1111
1122
|
}
|
|
1112
1123
|
}
|
package/schema/doc.schema.json
CHANGED
|
@@ -334,7 +334,33 @@
|
|
|
334
334
|
},
|
|
335
335
|
"cursor": {
|
|
336
336
|
"type": "object",
|
|
337
|
-
"description": "Cursor rendering (visible, hideWhenIdle, smoothing, size, style, click effects). See CursorStyle in @vosjs/studio-core. `style: \"arrow\"` (the default for new takes) draws an OS-style pointer with its tip on the recorded point; `\"default\"` (every earlier take) draws the white dot. `visible: false` hides the drawn pointer only \u2014 the track still drives cursor-follow zoom, and click effects keep drawing (silence those with clickFx.style: \"none\"). `hideWhenIdle` (default true) fades the dot out after ~1s parked and back in when it moves; scrolling is not movement, and it is always back at full opacity on a click.",
|
|
337
|
+
"description": "Cursor rendering (visible, hideWhenIdle, smoothing, size, style, click effects). See CursorStyle in @vosjs/studio-core. `style: \"arrow\"` (the default for new takes) draws an OS-style pointer with its tip on the recorded point; `\"default\"` (every earlier take) draws the white dot. `visible: false` hides the drawn pointer only \u2014 the track still drives cursor-follow zoom, and click effects keep drawing (silence those with clickFx.style: \"none\"). `hideWhenIdle` (default true) fades the dot out after ~1s parked and back in when it moves; scrolling is not movement, and it is always back at full opacity on a click. `off` hides the drawn pointer over time ranges.",
|
|
338
|
+
"properties": {
|
|
339
|
+
"off": {
|
|
340
|
+
"type": "array",
|
|
341
|
+
"description": "Time ranges in which the drawn pointer is hidden, in OUTPUT seconds (the timeline the viewer sees, like overlays, so a range stays put through trims and speed changes). `{ in, out?, from? }`: an absent `out` hides it to the end of the film. The pointer fades out over 0.15 s before `in` and back in over 0.15 s after `out`, so the range itself is fully hidden; overlapping ranges read as their union. The track still drives cursor-follow zoom and click effects, as with `visible: false`. `from` marks a range something else placed (`endcard`: the end card's, written by `vos plan` and replaced or removed with the end card); a range without one is yours and a re-plan never touches it.",
|
|
342
|
+
"items": {
|
|
343
|
+
"type": "object",
|
|
344
|
+
"required": ["in"],
|
|
345
|
+
"properties": {
|
|
346
|
+
"in": {
|
|
347
|
+
"type": "number",
|
|
348
|
+
"minimum": 0,
|
|
349
|
+
"description": "OUTPUT seconds the pointer is gone from."
|
|
350
|
+
},
|
|
351
|
+
"out": {
|
|
352
|
+
"type": "number",
|
|
353
|
+
"description": "OUTPUT seconds it returns at, past `in`. Absent = to the end of the film."
|
|
354
|
+
},
|
|
355
|
+
"from": {
|
|
356
|
+
"type": "string",
|
|
357
|
+
"description": "What placed it (e.g. `endcard`). Absent = a person's own."
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
"additionalProperties": false
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
},
|
|
338
364
|
"additionalProperties": true
|
|
339
365
|
},
|
|
340
366
|
"cam": {
|
package/skills/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.27.0
|
|
@@ -244,3 +244,10 @@ by data. Never spread before the seed is signed off.
|
|
|
244
244
|
- A hand-typed dimension: every size comes from `channel-specs.json`.
|
|
245
245
|
- The save beat on a demo instance that disables writes: say so in
|
|
246
246
|
`skipped`, never fake the click.
|
|
247
|
+
- A zoom trusted because the track lists it: `frames --at-zooms` renders
|
|
248
|
+
each apex beside its zoom-off twin and names one that matches; read the
|
|
249
|
+
stills before `deliver`, never the list.
|
|
250
|
+
- A rehearsal that presses a step that spends or creates in the shipped
|
|
251
|
+
product: `--dry-run` CLICKS. Guard the spend (`"guard": "spend"` + `id`,
|
|
252
|
+
recorded only with `--confirm <id>`) and write the side-effect ledger
|
|
253
|
+
before the first rehearsal (the `product-video` skill's pre-flight).
|
|
@@ -52,6 +52,69 @@ failure / 3 no browser / 4 the recorder met a sign-in instead of the page
|
|
|
52
52
|
| **Quick demo** (issue, PR, chat) | 1280×720 | webm, defaults | speed over polish; drafts acceptable here ONLY |
|
|
53
53
|
|
|
54
54
|
Per-channel dimensions and byte budgets: `references/destinations.md`.
|
|
55
|
+
A **how-to** (one flow in a product, from a door to its result, for a docs
|
|
56
|
+
page or a changelog) takes the quick demo's viewport or the launch video's,
|
|
57
|
+
and its own grammar: `references/how-to.md`.
|
|
58
|
+
|
|
59
|
+
## Pre-flight: six answers before the first take
|
|
60
|
+
|
|
61
|
+
A flow in somebody's product has consequences the footage does not show:
|
|
62
|
+
two rehearsals left two untitled drafts in a founder's projects and both
|
|
63
|
+
appeared in the recorded sidebar; a rehearsal that reaches "Generate"
|
|
64
|
+
spends a credit; a result that arrives twelve minutes later is a second
|
|
65
|
+
take without a plan for it. Answer these BEFORE `vos look`, the script or a
|
|
66
|
+
rehearsal. **Each answer either changes the script or is said to the
|
|
67
|
+
person before recording**; an answer that does neither was not an answer.
|
|
68
|
+
|
|
69
|
+
1. **The door.** Which way through the product does the video walk, and
|
|
70
|
+
which others exist (a guided flow, a prompt box, an apps menu, a
|
|
71
|
+
template)? A how-to walks ONE. When there are several, list them and
|
|
72
|
+
confirm the door with the person before scripting; a door that works
|
|
73
|
+
under automation beats a recommended one that does not, and hiding the
|
|
74
|
+
automation from the site is never the answer (find another door, or
|
|
75
|
+
hand the shot list to a person: `references/sessions.md`, rung 4).
|
|
76
|
+
2. **Whose account, face and work.** What the signed-in account shows ships
|
|
77
|
+
in the video: its avatar, its name, its recent items, the titles of its
|
|
78
|
+
other projects. A demo account is the answer; when the person records
|
|
79
|
+
from their own, say what will show before the take. The rehearsal
|
|
80
|
+
reports an address, a key or a card as EXPOSED (mask those), and the
|
|
81
|
+
account's own name and its recent lists as NOTICES: those are the
|
|
82
|
+
person's call. Say they will be on screen and offer the one-line `mask`;
|
|
83
|
+
never mask a name by default, and a face is yours to notice and theirs
|
|
84
|
+
to decide.
|
|
85
|
+
3. **What the flow CREATES.** Write the side-effect ledger: for each step,
|
|
86
|
+
what it leaves in the product (a draft, a project, a message, a file, a
|
|
87
|
+
row), remembering that `--dry-run` CLICKS, so every rehearsal creates
|
|
88
|
+
them too. Say the ledger to the person before the first rehearsal when
|
|
89
|
+
the product is a real account, and report it at the end so they can
|
|
90
|
+
delete what was made. A product where creating is cheap (a sandbox, a
|
|
91
|
+
seeded local instance) needs the ledger written, not asked.
|
|
92
|
+
4. **What it SPENDS.** A step that costs something real (a generation, a
|
|
93
|
+
purchase, a message sent, a deploy) carries `"guard": "spend"` and an
|
|
94
|
+
`id`. A rehearsal hovers it and never presses it; `vos record` refuses
|
|
95
|
+
the take before a browser opens unless the command names the step
|
|
96
|
+
(`--confirm <id>`), and the done event records that it was pressed.
|
|
97
|
+
Name the spend to the person in words ("one generation") and get the
|
|
98
|
+
yes before the record; the consent then lives in the file and the
|
|
99
|
+
command line, where a reviewer can read it.
|
|
100
|
+
5. **Is the result async, and how long.** A product that finishes later (a
|
|
101
|
+
render, a generation, a deploy) is ONE take with an `await` step: the
|
|
102
|
+
capture stops, the page is polled off camera, the capture resumes when
|
|
103
|
+
the predicate holds, and the cut carries the measured time
|
|
104
|
+
(`{elapsed}` in the next caption; `references/how-to.md`). Wait for
|
|
105
|
+
what only the NEW result has (a percentage that appears, then goes;
|
|
106
|
+
never a title an older item shares), size `timeout` from what the
|
|
107
|
+
product measured last time, and `download` the output into
|
|
108
|
+
`take/outputs/` so the join can play it. On the hosted recorder the
|
|
109
|
+
waits come out of the footage cap: say how long before choosing it.
|
|
110
|
+
6. **Does sound matter.** The recorder captures frames. Sound a page plays
|
|
111
|
+
through a `<video>` or `<audio>` element is kept (`@vosjs/cli` 0.79 and
|
|
112
|
+
later: each heard source lands in `meta.media` and the plan places a
|
|
113
|
+
clip per interval); sound a page synthesizes with Web Audio is not
|
|
114
|
+
heard, and a downloaded output's own sound rides as a clip through
|
|
115
|
+
`vos join`. When the sound IS the point (a voice preview, a generated
|
|
116
|
+
clip) and the product synthesizes it, say so before recording and plan
|
|
117
|
+
the download; when it is not, say the video is silent and score it.
|
|
55
118
|
|
|
56
119
|
## The core loop (every destination)
|
|
57
120
|
|
|
@@ -81,6 +144,16 @@ Per-channel dimensions and byte budgets: `references/destinations.md`.
|
|
|
81
144
|
take directory and out of git, and record from a demo account: what the
|
|
82
145
|
account shows ships in the video.
|
|
83
146
|
|
|
147
|
+
**Never script a signed-in page blind.** `vos look <url> --session <app>`
|
|
148
|
+
(or `--storage-state`; `@vosjs/cli` 0.78 and later) opens the page
|
|
149
|
+
through the door the recorder will use and prints a screenshot plus
|
|
150
|
+
every control in view with ONE proposed selector each, counted UNIQUE
|
|
151
|
+
or AMBIGUOUS by the recorder's own engine; `--click <selector>` shows
|
|
152
|
+
what a click opens before the script relies on it, and refuses a control
|
|
153
|
+
whose words mean spend, delete or submit. Take UNIQUE selectors into
|
|
154
|
+
steps as they are. Then the side-effect ledger (pre-flight, 3), then
|
|
155
|
+
rehearse.
|
|
156
|
+
|
|
84
157
|
**Verified the feature with agent-browser already?** Keep that walk and
|
|
85
158
|
skip the second script. agent-browser's `--json` result does not say
|
|
86
159
|
what ran (`scroll` answers `{scrolled:true}`), so wrap each call so the
|
|
@@ -170,9 +243,13 @@ Per-channel dimensions and byte budgets: `references/destinations.md`.
|
|
|
170
243
|
`references/sessions.md`.
|
|
171
244
|
|
|
172
245
|
3. **Record**: `vos record --actions actions.json --out take --strict --json`
|
|
173
|
-
`--strict` always: skipped selector
|
|
174
|
-
|
|
175
|
-
|
|
246
|
+
`--strict` always: a skipped selector, a failed `expect`, a click on a
|
|
247
|
+
disabled control, a timed-out `await` or a capped take → exit 2 with
|
|
248
|
+
`skipped[]` in the done event (a first load that never goes network-idle
|
|
249
|
+
is a note, not a failure, since `@vosjs/cli` 0.78). A skip means the
|
|
250
|
+
flow is broken — fix it, never ship around it. A guarded step records
|
|
251
|
+
only with `--confirm <id>` (pre-flight, 4). The take auto-encodes and
|
|
252
|
+
auto-plans.
|
|
176
253
|
(`vos create --actions actions.json out.webm --strict` is the one-shot
|
|
177
254
|
record+render verb — fine for a quick first pass, but THIS skill's loop
|
|
178
255
|
reviews frames before rendering, so prefer the separate verbs here.)
|
|
@@ -295,3 +372,14 @@ when the ask is a release, not one video.
|
|
|
295
372
|
one per gesture): mark the payoff steps and plan `--zooms per-beat`.
|
|
296
373
|
- Trusting `--strict` for the scene: it proves the steps ran, not that the
|
|
297
374
|
app holds what the beat shows. Check with `expect` + `js` + `equals`.
|
|
375
|
+
- Trusting a zoom because the track lists it: two of a film's four zooms
|
|
376
|
+
never rendered and the list said they did. `frames --at-zooms` renders
|
|
377
|
+
each apex beside its zoom-off twin and names an apex that matches its
|
|
378
|
+
twin; read the stills, never the list.
|
|
379
|
+
- A rehearsal that presses a step that spends: `--dry-run` CLICKS. A
|
|
380
|
+
spending step carries `"guard": "spend"` and an `id`, so the rehearsal
|
|
381
|
+
hovers it and `record` refuses it without `--confirm <id>`; a step that
|
|
382
|
+
only creates goes in the side-effect ledger before the first rehearsal.
|
|
383
|
+
- Scripting a signed-in product blind: `vos look` first, then the ledger,
|
|
384
|
+
then the rehearsal. Guessing selectors from a public page costs a take
|
|
385
|
+
per guess.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# The how-to: one flow, one door, the result first
|
|
2
|
+
|
|
3
|
+
A how-to shows a person how to reach ONE result in a product: "make a
|
|
4
|
+
cinematic shot in HeyGen", "put a video on vos.so". It is the clip a docs
|
|
5
|
+
page, a changelog entry or a support answer embeds, and it is judged by
|
|
6
|
+
whether a viewer can repeat the path. The marketing rules in `taste.md`
|
|
7
|
+
still hold (one idea, open on the money shot, end settled); these are the
|
|
8
|
+
rules that are specific to the genre. They came out of three agent-made
|
|
9
|
+
how-tos and the cuts a person had to redo on them.
|
|
10
|
+
|
|
11
|
+
## The grammar
|
|
12
|
+
|
|
13
|
+
1. **One door.** A product usually has several ways to the same result
|
|
14
|
+
(a guided flow, a prompt box, an apps menu, a template). The video walks
|
|
15
|
+
ONE, and the person named it before the script was written (the
|
|
16
|
+
pre-flight in `SKILL.md`). Two doors in one clip read as two videos;
|
|
17
|
+
a door that works under automation beats the recommended one that does
|
|
18
|
+
not (never hide the automation from a site; pick the door that works, or
|
|
19
|
+
hand the shot list to a person).
|
|
20
|
+
|
|
21
|
+
2. **One caption per step, in the product's own words for the control.**
|
|
22
|
+
The caption names the control the way the product labels it, as a verb
|
|
23
|
+
phrase: "Open Apps", "Choose Cinematic Shots", "Press Generate". Not a
|
|
24
|
+
sentence about the feature, not marketing copy, nothing the footage does
|
|
25
|
+
not show. It sits on the step whose action it describes (`caption` on
|
|
26
|
+
the step in `actions.json`; `vos plan` and `vos join --captions` land
|
|
27
|
+
each one anchored to its step, so a re-record carries it). One caption
|
|
28
|
+
is one beat; a step with no caption is part of the beat before it.
|
|
29
|
+
|
|
30
|
+
3. **The result is the payoff, and the cold open.** Open on the finished
|
|
31
|
+
result for a beat (a repeated segment at camera rest,
|
|
32
|
+
`segments[].camera: "rest"`, the `vos-cut` skill's "Assemble"), then
|
|
33
|
+
the path from the door, then the result again, settled, as the last
|
|
34
|
+
frame. The result's step is the beat marked `"zoom": true`; the path's
|
|
35
|
+
clicks stay wide unless one of them is the point. `still` sits on the
|
|
36
|
+
result, never on the door.
|
|
37
|
+
|
|
38
|
+
4. **A recap card that lists the path.** The last thing on screen is the
|
|
39
|
+
path as a list, in the captions' own words, in order ("Apps → Cinematic
|
|
40
|
+
Shots → Describe the shot → Generate"), on the plate after the card
|
|
41
|
+
fades (`frame.anim.exit: "fade"` plus layers past the footage), never
|
|
42
|
+
over a frozen frame. A viewer who looked away for the middle leaves with
|
|
43
|
+
the path.
|
|
44
|
+
|
|
45
|
+
5. **Measured times only.** When the product takes time off camera (`await`
|
|
46
|
+
in the script), the cut says how long it took in the words the
|
|
47
|
+
measurement gives: `{elapsed}` in the caption after the wait prints the
|
|
48
|
+
measured time ("8 minutes later, the shot is ready"), or write the
|
|
49
|
+
number read from `meta.skips[].ms` ("About 8 minutes later"). Never a
|
|
50
|
+
round number nobody measured, never "instantly" or "in seconds", and
|
|
51
|
+
never a speed read off a capture (the recorder loads the page too).
|
|
52
|
+
|
|
53
|
+
6. **Music lands on the result.** `vos score --track <slug> --at <result
|
|
54
|
+
step id>` puts the track's swell on the payoff; a how-to with a voice
|
|
55
|
+
or the product's own sound (a generated video playing) keeps the bed
|
|
56
|
+
under it (`--gain`). The downloaded output's own sound rides as a clip
|
|
57
|
+
through `vos join`.
|
|
58
|
+
|
|
59
|
+
7. **As long as its path.** A how-to is not held to the 30 s marketing
|
|
60
|
+
band; it is held to its beats: about 1 s of reading after a page
|
|
61
|
+
changes, 0.6 s after a control, the waits cut out, nothing padded.
|
|
62
|
+
Run 3's how-to was 36 s for seven steps and an 8-minute generation.
|
|
63
|
+
|
|
64
|
+
## The checks, in order
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
vos validate actions.json # the captions sit on the steps they describe
|
|
68
|
+
vos record --actions actions.json --out take --dry-run # the door opens, the guard hovers, the wait is timed
|
|
69
|
+
vos record --actions actions.json --out take --strict --json --confirm <spend id>
|
|
70
|
+
vos plan take --zooms per-beat --still <result output s>
|
|
71
|
+
vos frames take --at-zooms # every apex beside its zoom-off twin
|
|
72
|
+
vos frames take --times <each caption start + 1> # every caption on its own ground, in the product's words
|
|
73
|
+
vos check-video out.mp4 # one cut per 5 s at most, the first and last frames stand alone
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
A caption the footage does not show, a time that was not measured, a second
|
|
77
|
+
door, or a result that is not the first and last frame each send the cut
|
|
78
|
+
back a round, not the recording.
|
|
@@ -25,6 +25,10 @@ The rubric for judging a take before its render ships. Judge stills
|
|
|
25
25
|
Launch videos may run longer but every scene must still earn its seconds.
|
|
26
26
|
3. **End settled.** Close on a resolved state (result visible, motion parked),
|
|
27
27
|
never mid-action — the loop point should read as intentional.
|
|
28
|
+
A **how-to** (one flow to one result, for a docs page or a changelog)
|
|
29
|
+
keeps all three and adds its own: one door, one caption per step in
|
|
30
|
+
the product's words for the control, the result as the cold open and
|
|
31
|
+
the last frame, a recap card, measured times only (`how-to.md`).
|
|
28
32
|
|
|
29
33
|
## Flow authoring (actions.json)
|
|
30
34
|
|